Riduci le dimensioni dell'OTA

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

Gli aggiornamenti OTA di Android a volte contengono file modificati che non corrispondono a modifiche del codice. Sono artefatti del sistema di build. Ciò può verificarsi 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 build progettate per ridurre le dimensioni delle patch OTA. Sono state eliminate le modifiche non necessarie ai file tra le build e negli aggiornamenti OTA sono contenuti solo i file correlati alle patch. AOSP include anche uno strumento di differenze di build, che filtra le modifiche comuni ai file correlate alla build per fornire una differenza più pulita dei file di build, e uno strumento di mappatura dei blocchi, che ti aiuta a mantenere coerente l'allocazione dei blocchi.

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

  • Utilizzo di ZSTD, un algoritmo di compressione senza perdita per 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
  • Aumentare le dimensioni della finestra di compressione utilizzata durante l'OTA. La dimensione massima della finestra di compressione può essere impostata personalizzando il parametro di build nel file .mk di un dispositivo. Questa variabile è impostata come PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 262144
  • Utilizzo della ricompressione Puffin, uno strumento di applicazione di patch deterministico per i flussi deflate, che gestisce le funzioni di compressione e differenze per la generazione di aggiornamenti OTA A/B.
  • Modifiche all'utilizzo dello strumento di generazione delta, ad esempio la modalità di utilizzo della libreria bsdiff per la compressione delle patch. In Android 9 e versioni successive, lo strumento bsdiff seleziona l'algoritmo di compressione che offre i migliori risultati di compressione per una patch.
  • I miglioramenti apportati a update_engine hanno comportato un minor consumo di memoria quando vengono applicate patch per gli aggiornamenti dei dispositivi A/B.

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

Ordine dei file

Problema: i file system 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 checkout. Strumenti come ls ordinano i risultati per impostazione predefinita, ma la funzione jolly utilizzata dai comandi come find e make non esegue l'ordinamento. Prima di utilizzare questi strumenti, devi ordinare gli output.

Soluzione: quando utilizzi strumenti come find e make con la funzione jolly, ordina l'output di questi comandi prima di utilizzarli. Quando utilizzi $(wildcard) o $(shell find) nei file Android.mk, 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 principale utilizzando la macro all-*-files-under integrata, che include all-cpp-files-under (poiché diverse definizioni erano distribuite in altri makefile). Per maggiori dettagli, consulta le seguenti risorse:

Directory di build

Problema: la modifica della directory in cui vengono compilati gli elementi può causare la diversità dei file binari. La maggior parte dei percorsi nella build 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-rimosso, quindi cambierà se cambiano i simboli di debug.

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

Timestamp

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

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

Soluzioni/Esempi: per rimuovere i timestamp dall'output della build, 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 diversi, quindi non utilizzarle. Di seguito sono riportate alcune opzioni per eliminare queste macro:

Timestamp incorporati negli archivi (zip, jar)

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

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 i dettagli, consulta la 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 i dettagli, consulta la CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028.

Stringhe di versione

Problema: le stringhe di versione APK spesso avevano BUILD_NUMBER aggiunto alle versioni hardcoded. Anche se non è cambiato nient'altro in un APK, di conseguenza l'APK sarebbe comunque diverso.

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

Esempi:

Abilitare il calcolo di integrità sul dispositivo

Se dm-verity è abilitato sul dispositivo, gli strumenti OTA rilevano automaticamente la configurazione di verifica e abilitano il calcolo della verifica sul dispositivo. Ciò consente di calcolare i blocchi di verifica sui dispositivi Android anziché memorizzarli come byte non elaborati nel pacchetto OTA. I blocchi di verifica possono utilizzare circa 16 MB per una partizione da 2 GB.

Tuttavia, la verifica del calcolo sul dispositivo può richiedere molto tempo. In particolare, il codice di correzione degli errori di trasmissione può richiedere molto tempo. Sui dispositivi Pixel, in genere ci vogliono fino a 10 minuti. Sui dispositivi di fascia bassa potrebbe essere necessario più tempo. Se vuoi disattivare il calcolo della verifica on-device, ma vuoi comunque attivare dm-verity, puoi farlo passando --disable_fec_computation allo strumento ota_from_target_files durante la generazione di 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 dispositivo non è attivato dm-verity, il passaggio di questo flag non ha alcun effetto.

Strumenti di build coerenti

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

Soluzioni/Esempi:sono state necessarie modifiche ai seguenti strumenti di compilazione:

Utilizzare lo strumento di confronto delle build

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

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

Per utilizzare lo strumento di confronto delle build, esegui questo comando:

target_files_diff.py dir1 dir2

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

Mantenere 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'updater deve eseguire I/O non necessari per spostare i blocchi per un aggiornamento OTA.

In un aggiornamento OTA A/B virtuale, l'I/O non necessario può aumentare notevolmente lo spazio di archiviazione richiesto per archiviare lo snapshot copy-on-write. In un aggiornamento OTA non A/B, lo spostamento dei blocchi per un aggiornamento OTA contribuisce al tempo di aggiornamento poiché c'è più I/O a causa degli spostamenti dei blocchi.

Per risolvere questo problema, in Android 7.0 Google ha esteso lo strumento make_ext4fs per mantenere l'allocazione dei blocchi coerente 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 di mappatura 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 archiviati 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 ciò non contribuisca 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 necessario per applicare l'OTA.

Evita di aggiornare le app

Oltre a ridurre al minimo le differenze tra le 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 in un aggiornamento OTA può avere un impatto significativo sulle dimensioni dei pacchetti OTA e fornire pochi vantaggi agli utenti. Quando gli utenti ricevono un pacchetto OTA, potrebbero avere già l'app aggiornata o una versione ancora più recente, ricevuta direttamente dagli store.