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 comePRODUCT_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 strumentobsdiff
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:
- 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 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:
- Rimuovili. Per un esempio, consulta https://android.googlesource.com/platform/system/core/+/30622bbb209db187f6851e4cf0cdaa147c2fca9f.
- Per identificare in modo univoco il binario in esecuzione, leggi l'ID build dall'intestazione ELF.
-
Per sapere quando è stato creato il sistema operativo, leggi
ro.build.date
(questo vale per tutto 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)
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:
- https://android.googlesource.com/platform/packages/apps/Camera2/+/5e0f4cf699a4c7c95e2c38ae3babe6f20c258d27
- https://android.googlesource.com/platform/build/+/d75d893da8f97a5c7781142aaa7a16cf1dbb669c
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:
- Creatore di file NOTICE. Il creatore del file NOTICE è stato modificato per creare raccolte NOTICE riproducibili. Fai riferimento a CL: https://android.googlesource.com/platform/build/+/8ae4984c2c8009e7a08e2a76b1762c2837ad4f64.
- Java Android Compiler Kit (Jack). La toolchain Jack richiedeva un aggiornamento per gestire le modifiche occasionali nell'ordinamento dei costruttori generati. Sono stati aggiunti accessor deterministici per i costruttori alla toolchain: https://android.googlesource.com/toolchain/jack/+/056a5425b3ef57935206c19ecb198a89221ca64b.
- Compilatore ART AOT (dex2oat). Il 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 build crea un file
/system/lib/libpac.so
diverso perché lo snapshot V8 cambia per ogni build. La soluzione è stata rimuovere lo snapshot: https://android.googlesource.com/platform/external/v8/+/e537f38c36600fd0f3026adba6b3f4cbcee1fb29. - File di pre-dexopt dell'applicazione (.odex). I file pre-dexopt (.odex) contenevano un padding non inizializzato sui sistemi a 64 bit. È stato corretto: https://android.googlesource.com/platform/art/+/34ed3afc41820c72a3c0ab9770be66b6668aa029.
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.