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 suPRODUCT_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 strumentobsdiff
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:
- 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
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:
- Rimuovili. Per un esempio, consulta https://android.googlesource.com/platform/system/core/+/30622bbb209db187f6851e4cf0cdaa147c2fca9f.
- Per identificare in modo univoco il file binario in esecuzione, leggi l'ID build dall'intestazione ELF.
-
Per sapere quando è stato compilato il sistema operativo, leggi il file
ro.build.date
(funziona per tutti gli elementi tranne le build incrementali, che potrebbero non aggiornare questa data). Per un esempio, consulta https://android.googlesource.com/platform/external/libchrome/+/8b7977eccc94f6b3a3896cd13b4aeacbfa1e0f84.
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:
- https://android.googlesource.com/platform/packages/apps/Camera2/+/5e0f4cf699a4c7c95e2c38ae3babe6f20c258d27
- https://android.googlesource.com/platform/build/+/d75d893da8f97a5c7781142aaa7a16cf1dbb669c
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:
- Creator di file NOTICE. Lo strumento per la creazione di file NOTICE è stato modificato per creare raccolte di NOTIZIE riproducibili. Fai riferimento al CL: https://android.googlesource.com/platform/build/+/8ae4984c2c8009e7a08e2a76b1762c2837ad4f64.
- Java Android Compiler Kit (Jack). La toolchain Jack ha richiesto un aggiornamento per gestire le modifiche occasionali nell'ordine dei costruttori generati. Alla toolchain sono stati aggiunti gli accessori deterministici per i metodi di inizializzazione: https://android.googlesource.com/toolchain/jack/+/056a5425b3ef57935206c19ecb198a89221ca64b.
- Compilatore AOT ART (dex2oat). Il codice binario del compilatore ART ha ricevuto un aggiornamento che ha aggiunto un'opzione per creare un'immagine deterministica: https://android.googlesource.com/platform/art/+/ace0dc1dd5480ad458e622085e51583653853fb9.
-
Il file libpac.so (V8). Ogni compilazione crea un file
/system/lib/libpac.so
diverso perché l'istantanea V8 cambia per ogni compilazione. La soluzione è stata rimuovere lo snapshot: https://android.googlesource.com/platform/external/v8/+/e537f38c36600fd0f3026adba6b3f4cbcee1fb29. - File pre-dexopt (.odex) dell'applicazione. I file pre-dexopt (.odex) contenevano spazi inizializzati su sistemi a 64 bit. Il problema è stato risolto: https://android.googlesource.com/platform/art/+/34ed3afc41820c72a3c0ab9770be66b6668aa029.
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.