Riduci le dimensioni dell'OTA

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

Gli aggiornamenti OTA di Android contengono occasionalmente file modificati che non corrispondono alle modifiche del codice. In realtà creano artefatti di sistema. Ciò può verificarsi quando lo stesso codice, creato in tempi diversi, da directory diverse o su macchine diverse, produce un gran numero di file modificati. Tali 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'OTA, AOSP include modifiche al sistema di build progettate per ridurre la dimensione delle patch OTA. Le modifiche non necessarie ai file tra le build sono state eliminate e solo i file relativi alle patch sono contenuti negli aggiornamenti OTA. AOSP include anche uno strumento diff di build , che filtra le modifiche comuni ai file relativi alla build per fornire un diff di file di build più pulito, e uno strumento di mappatura dei blocchi , che aiuta a mantenere coerente l'allocazione dei blocchi.

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

  • Utilizzo di Brotli , un algoritmo di compressione senza perdite per scopi generici per immagini complete su aggiornamenti di dispositivi non A/B. Brotli può essere personalizzato per ottimizzare la compressione. Negli aggiornamenti più grandi composti da due o più blocchi nel file system (ad esempio system.img ), i produttori o i partner dei dispositivi possono aggiungere i propri algoritmi di compressione e utilizzare algoritmi di compressione diversi su blocchi diversi dello stesso aggiornamento.
  • Utilizzo della ricompressione Puffin , uno strumento di patch deterministico per i flussi deflate, che gestisce le funzioni di compressione e diff per la generazione di aggiornamenti A/B OTA.
  • Modifiche all'utilizzo dello strumento di generazione delta, ad esempio il modo in cui la libreria bsdiff viene utilizzata per comprimere le patch. In Android 9 e versioni successive, lo strumento bsdiff seleziona l'algoritmo di compressione che fornirebbe i migliori risultati di compressione per una patch.
  • I miglioramenti apportati a update_engine hanno comportato un minore consumo di memoria quando vengono applicate le patch per gli aggiornamenti dei dispositivi A/B.
  • Miglioramenti alla suddivisione di file zip di grandi dimensioni per aggiornamenti OTA basati su blocchi. Una modalità in imgdiff divide i file APK di grandi dimensioni, in base ai nomi delle voci. Ciò produce una patch più piccola rispetto alla divisione lineare dei file e all'utilizzo dello strumento bsdiff per comprimerli.

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

Ordine dei file

Problema : i file system non garantiscono un ordine di file quando viene richiesto un elenco di file in una directory, sebbene di solito sia lo stesso per lo stesso checkout. Strumenti come ls ordinano i risultati per impostazione predefinita, ma la funzione jolly utilizzata da comandi come find e make non ordina. Prima di utilizzare questi strumenti, è necessario ordinare gli output.

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

Esempi: molte istanze sono state corrette nel sistema di build principale utilizzando la macro incorporata all-*-files-under , che include all-cpp-files-under (poiché diverse definizioni erano sparse in altri makefile). Per i dettagli, fare riferimento a quanto segue:

Crea directory

Problema: la modifica della directory in cui vengono create le cose può far sì che i file binari siano diversi. La maggior parte dei percorsi nella build 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 il .note.gnu.build-id viene generato dall'hashing del binario pre-rimosso, quindi cambierà se cambiano i simboli di debug.

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

Timestamp

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

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

Soluzioni/Esempi: per rimuovere i timestamp dall'output della build, utilizzare le istruzioni fornite 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. Ecco 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 usi del comando zip . Ciò ha rimosso l'UID/GID del builder e il timestamp Unix esteso dal file zip.

Un nuovo strumento, ziptime (situato in /platform/build/+/main/tools/ziptime/ ) reimposta i normali timestamp nelle intestazioni zip. Per i dettagli, fare riferimento al 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, fare riferimento al CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028 .

Stringhe di versione

Problema: le stringhe della versione APK spesso avevano BUILD_NUMBER aggiunto alle versioni codificate. Di conseguenza, anche se non cambiasse nient'altro in un APK, l'APK sarebbe comunque diverso.

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

Esempi:

Abilita il calcolo della verità sul dispositivo

Se dm-verity è abilitato sul tuo dispositivo, gli strumenti OTA rilevano automaticamente la configurazione della verità e abilitano il calcolo della verità sul dispositivo. Ciò consente di calcolare i blocchi di verità sui dispositivi Android, invece di essere archiviati come byte grezzi nel pacchetto OTA. I blocchi Verity possono utilizzare circa 16 MB per una partizione da 2 GB.

Tuttavia, la verifica della verità informatica sul dispositivo può richiedere molto tempo. Nello specifico, il codice di correzione degli errori di inoltro può richiedere molto tempo. Sui dispositivi Pixel, l'operazione richiede fino a 10 minuti. Sui dispositivi di fascia bassa potrebbe richiedere più tempo. Se desideri disabilitare il calcolo della verità sul dispositivo, ma abilitare comunque dm-verity, puoi farlo passando --disable_fec_computation allo strumento ota_from_target_files quando generi un aggiornamento OTA. Questo flag disabilita il calcolo della verità sul dispositivo durante gli aggiornamenti OTA. Diminuisce il tempo di installazione OTA, ma aumenta le dimensioni del pacchetto OTA. Se sul tuo dispositivo non è abilitato dm-verity, il passaggio di questo flag non ha alcun effetto.

Strumenti di creazione coerenti

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

Soluzioni/Esempi: erano necessarie modifiche ai seguenti strumenti di creazione:

Utilizza lo strumento build diff

Per i casi in cui non è possibile eliminare le modifiche ai file relative alla build, AOSP include uno strumento diff di build, target_files_diff.py da utilizzare per confrontare due pacchetti di file. Questo strumento esegue una differenza ricorsiva tra due build, escludendo le modifiche comuni ai file relativi alla build, come

  • 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 corrente.

Per utilizzare lo strumento build diff, esegui il comando seguente:

target_files_diff.py dir1 dir2

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

Mantieni coerente l'allocazione dei blocchi

Per un dato file, anche se il suo contenuto rimane lo stesso tra due build, i blocchi effettivi che contengono i dati potrebbero essere cambiati. Di conseguenza, il programma di aggiornamento deve eseguire I/O non necessari per spostare i blocchi per un aggiornamento OTA.

In un aggiornamento OTA A/B virtuale, gli I/O non necessari possono 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 ad aumentare il 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 coerente l'allocazione dei blocchi tra le build. Lo strumento make_ext4fs accetta un flag opzionale -d base_fs che tenta di allocare file agli stessi blocchi durante la generazione di un'immagine ext4 . È possibile estrarre i file di mappatura dei blocchi (come i file di mappa base_fs ) dal file zip dei file di destinazione di una build precedente. Per ogni partizione ext4 , c'è 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 aiuti a ridurre le dimensioni complessive del pacchetto OTA, migliora le prestazioni degli aggiornamenti 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 app store. Gli APK spesso costituiscono una parte significativa di varie partizioni su un dispositivo. Includere le ultime versioni delle app che vengono aggiornate dagli app store in un aggiornamento OTA potrebbe avere un impatto significativo sui pacchetti OTA e offrire scarsi vantaggi per l'utente. Nel momento in cui gli utenti ricevono un pacchetto OTA, potrebbero già avere l'app aggiornata, o una versione ancora più recente, ricevuta direttamente dagli app store.