Diese Seite beschreibt Änderungen, die zu AOSP hinzugefügt wurden, um unnötige Dateiänderungen zwischen Builds zu reduzieren. Geräteimplementierer, die ihre eigenen Build-Systeme verwalten, können diese Informationen als Leitfaden zum Reduzieren der Größe ihrer OTA-Updates (Over-the-Air) verwenden.
Android-OTA-Updates enthalten gelegentlich geänderte Dateien, die nicht Codeänderungen entsprechen. Sie sind eigentlich Artefakte des Build-Systems. Dies kann vorkommen, wenn derselbe Code, der zu unterschiedlichen Zeiten, aus unterschiedlichen Verzeichnissen oder auf unterschiedlichen Computern erstellt wurde, eine große Anzahl geänderter Dateien erzeugt. Solche überschüssigen Dateien erhöhen die Größe eines OTA-Patches und erschweren die Bestimmung des geänderten Codes.
Um den Inhalt eines OTA transparenter zu machen, enthält AOSP Build-Systemänderungen, die darauf abzielen, die Größe von OTA-Patches zu reduzieren. Unnötige Dateiänderungen zwischen Builds wurden eliminiert, und in OTA-Updates sind nur Patch-bezogene Dateien enthalten. AOSP enthält auch ein Build-Diff-Tool , das häufige Build-bezogene Dateiänderungen herausfiltert, um einen saubereren Build-Datei-Diff bereitzustellen, und ein Block-Mapping-Tool , das Ihnen hilft, die Blockzuordnung konsistent zu halten.
Ein Build-System kann auf verschiedene Weise unnötig große Patches erstellen. Um dies abzumildern, wurden in Android 8.0 und höher neue Funktionen implementiert, um die Patchgröße für jeden Dateiunterschied zu reduzieren. Zu den Verbesserungen, die die Paketgröße von OTA-Updates reduziert haben, gehören die folgenden:
- Verwendung von Brotli , einem generischen, verlustfreien Komprimierungsalgorithmus für vollständige Bilder auf Nicht-A/B-Geräteaktualisierungen. Brotli kann angepasst werden, um die Komprimierung zu optimieren. Bei größeren Updates, die aus zwei oder mehr Blöcken im Dateisystem bestehen (z. B.
system.img
), können Gerätehersteller oder Partner ihre eigenen Komprimierungsalgorithmen hinzufügen und unterschiedliche Komprimierungsalgorithmen für verschiedene Blöcke desselben Updates verwenden. - Verwendung von Puffin Recompression, einem deterministischen Patching-Tool für Deflate-Streams, das die Komprimierungs- und Diff-Funktionen für die Generierung von A/B-OTA-Updates verarbeitet.
- Änderungen an der Verwendung des Delta-Generierungs-Tools, z. B. wie die
bsdiff
-Bibliothek zum Komprimieren von Patches verwendet wird. In Android 9 und höher wählt dasbsdiff
Tool den Komprimierungsalgorithmus aus, der die besten Komprimierungsergebnisse für einen Patch liefert. - Verbesserungen an der
update_engine
führten zu weniger Speicherverbrauch, wenn Patches für A/B-Geräteaktualisierungen angewendet wurden. - Verbesserungen beim Aufteilen großer ZIP-Dateien für blockbasierte OTA-Updates. Ein Modus in
imgdiff
teilt übergroße APK-Dateien basierend auf Eintragsnamen. Dies erzeugt einen kleineren Patch im Vergleich zum linearen Aufteilen von Dateien und der Verwendung des Toolsbsdiff
, um sie zu komprimieren.
In den folgenden Abschnitten werden verschiedene Probleme erörtert, die sich auf die Größe von OTA-Updates auswirken, ihre Lösungen und Beispiele für die Implementierung in AOSP.
Dateireihenfolge
Problem : Dateisysteme garantieren keine Dateireihenfolge, wenn sie nach einer Liste von Dateien in einem Verzeichnis gefragt werden, obwohl dies normalerweise für denselben Checkout gleich ist. Tools wie ls
sortieren die Ergebnisse standardmäßig, aber die Wildcard-Funktion, die von Befehlen wie find
und make
verwendet wird, sortiert nicht. Bevor Sie diese Tools verwenden, müssen Sie die Ausgaben sortieren.
Lösung : Wenn Sie Tools wie find
und make
mit der Platzhalterfunktion verwenden, sortieren Sie die Ausgabe dieser Befehle, bevor Sie sie verwenden. Wenn Sie $(wildcard)
oder $(shell find)
in Android.mk
Dateien verwenden, sortieren Sie diese ebenfalls. Einige Tools wie Java sortieren Eingaben. Stellen Sie daher vor dem Sortieren der Dateien sicher, dass das von Ihnen verwendete Tool dies nicht bereits getan hat.
Beispiele: Viele Instanzen wurden im Core-Build-System mit dem eingebauten all-*-files-under
Makro behoben, das all-cpp-files-under
enthält (da mehrere Definitionen in anderen Makefiles verteilt waren). Einzelheiten finden Sie unter:
- 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
Verzeichnis aufbauen
Problem: Das Ändern des Verzeichnisses, in dem die Dinge erstellt werden, kann dazu führen, dass die Binärdateien unterschiedlich sind. Die meisten Pfade im Android-Build sind relative Pfade, sodass __FILE__
in C/C++ kein Problem darstellt. Die Debug-Symbole codieren jedoch standardmäßig den vollständigen Pfadnamen, und die .note.gnu.build-id
wird durch Hashing der vorab entfernten Binärdatei generiert, sodass sie sich ändert, wenn sich die Debug-Symbole ändern.
Lösung: AOSP macht jetzt Debug-Pfade relativ. Einzelheiten finden Sie unter CL: https://android.googlesource.com/platform/build/+/6a66a887baadc9eb3d0d60e26f748b8453e27a02 .
Zeitstempel
Problem: Zeitstempel in der Build-Ausgabe führen zu unnötigen Dateiänderungen. Dies ist wahrscheinlich an den folgenden Orten der Fall:
-
__DATE__/__TIME__/__TIMESTAMP__
Makros in C- oder C++-Code. - In Zip-basierte Archive eingebettete Zeitstempel.
Lösungen/Beispiele: Um Zeitstempel aus der Build-Ausgabe zu entfernen, verwenden Sie die Anweisungen unten in __DATE__/__TIME__/__TIMESTAMP__ in C/C++. und Eingebettete Zeitstempel in Archiven .
__DATE__/__TIME__/__TIMESTAMP__ in C/C++
Diese Makros erzeugen immer unterschiedliche Ausgaben für verschiedene Builds, verwenden Sie sie also nicht. Hier sind einige Optionen zum Entfernen dieser Makros:
- Entferne sie. Ein Beispiel finden Sie unter https://android.googlesource.com/platform/system/core/+/30622bbb209db187f6851e4cf0cdaa147c2fca9f .
- Um die laufende Binärdatei eindeutig zu identifizieren, lesen Sie die Build-ID aus dem ELF-Header.
- Um zu wissen, wann das Betriebssystem erstellt wurde, lesen Sie das
ro.build.date
(dies funktioniert für alles außer inkrementellen Builds, die dieses Datum möglicherweise nicht aktualisieren). Ein Beispiel finden Sie unter https://android.googlesource.com/platform/external/libchrome/+/8b7977eccc94f6b3a3896cd13b4aeacbfa1e0f84 .
Eingebettete Zeitstempel in Archiven (zip, jar)
Android 7.0 hat das Problem eingebetteter Zeitstempel in Zip-Archiven behoben, indem allen Verwendungen des zip
Befehls -X
hinzugefügt wurde. Dadurch wurden die UID/GID des Builders und der erweiterte Unix-Zeitstempel aus der Zip-Datei entfernt.
Ein neues Tool, ziptime
(befindet sich in /platform/build/+/master/tools/ziptime/
) setzt die normalen Zeitstempel in den Zip-Headern zurück. Einzelheiten finden Sie in der README-Datei .
Das signapk
Tool setzt Zeitstempel für die APK-Dateien, die je nach Server-Zeitzone variieren können. Einzelheiten finden Sie in der CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028 .
Versionszeichenfolgen
Problem: Bei APK-Versionszeichenfolgen wurde häufig die BUILD_NUMBER
an ihre fest codierten Versionen angehängt. Selbst wenn sich sonst nichts in einem APK geändert hätte, wäre das APK immer noch anders.
Lösung: Entfernen Sie die Build-Nummer aus der APK-Versionszeichenfolge.
Lösung: Entfernen Sie die Build-Nummer aus der APK-Versionszeichenfolge.
Beispiele:
- https://android.googlesource.com/platform/packages/apps/Camera2/+/5e0f4cf699a4c7c95e2c38ae3babe6f20c258d27
- https://android.googlesource.com/platform/build/+/d75d893da8f97a5c7781142aaa7a16cf1dbb669c
Aktivieren Sie die Veritätsberechnung auf dem Gerät
Wenn dm-verity auf Ihrem Gerät aktiviert ist, übernehmen OTA-Tools automatisch Ihre Verity-Konfiguration und aktivieren die Verity-Berechnung auf dem Gerät. Dadurch können Verity-Blöcke auf Android-Geräten berechnet werden, anstatt als Rohbytes in Ihrem OTA-Paket gespeichert zu werden. Verity-Blöcke können ungefähr 16 MB für eine 2-GB-Partition verwenden.
Die Berechnung der Verity auf dem Gerät kann jedoch lange dauern. Insbesondere der Forward Error-Correction-Code kann sehr lange dauern. Auf Pixel-Geräten dauert es in der Regel bis zu 10 Minuten. Auf Low-End-Geräten kann es länger dauern. Wenn Sie die Verity-Berechnung auf dem Gerät deaktivieren, aber dennoch dm-verity aktivieren möchten, können Sie dies tun, indem Sie --disable_fec_computation
an das Tool ota_from_target_files
, wenn Sie ein OTA-Update generieren. Dieses Flag deaktiviert die Veritätsberechnung auf dem Gerät während OTA-Updates. Es verkürzt die OTA-Installationszeit, erhöht aber die OTA-Paketgröße. Wenn auf Ihrem Gerät dm-verity nicht aktiviert ist, hat das Übergeben dieses Flags keine Auswirkung.
Konsistente Build-Tools
Problem: Tools, die installierte Dateien generieren, müssen konsistent sein (eine bestimmte Eingabe sollte immer dieselbe Ausgabe erzeugen).
Lösungen/Beispiele: In den folgenden Build-Tools waren Änderungen erforderlich:
- HINWEIS Dateiersteller . Der NOTICE-Dateiersteller wurde geändert, um reproduzierbare NOTICE-Sammlungen zu erstellen. Siehe CL: https://android.googlesource.com/platform/build/+/8ae4984c2c8009e7a08e2a76b1762c2837ad4f64 .
- Java-Android-Compiler-Kit (Jack) . Die Jack-Toolchain erforderte ein Update, um gelegentliche Änderungen in der Reihenfolge der generierten Konstruktoren zu verarbeiten. Deterministische Accessoren für Konstruktoren wurden der Toolchain hinzugefügt: https://android.googlesource.com/toolchain/jack/+/056a5425b3ef57935206c19ecb198a89221ca64b .
- ART AOT-Compiler (dex2oat) . Die ART-Compiler-Binärdatei hat ein Update erhalten, das eine Option zum Erstellen eines deterministischen Bildes hinzugefügt hat: https://android.googlesource.com/platform/art/+/ace0dc1dd5480ad458e622085e51583653853fb9 .
- Die Datei libpac.so (V8) . Jeder Build erstellt eine andere Datei
/system/lib/libpac.so
, da sich der V8-Snapshot für jeden Build ändert. Die Lösung bestand darin, den Snapshot zu entfernen: https://android.googlesource.com/platform/external/v8/+/e537f38c36600fd0f3026adba6b3f4cbcee1fb29 . - Vorab-dexoptierte Dateien (.odex) der Anwendung . Die Dateien vor der Dexoptierung (.odex) enthielten nicht initialisiertes Padding auf 64-Bit-Systemen. Dies wurde korrigiert: https://android.googlesource.com/platform/art/+/34ed3afc41820c72a3c0ab9770be66b6668aa029 .
Mit dem Build-Diff-Tool
Für Fälle, in denen es nicht möglich ist, baubezogene Dateiänderungen zu eliminieren, enthält AOSP ein Build-Diff-Tool, target_files_diff.py
, das zum Vergleichen zweier Dateipakete verwendet wird. Dieses Tool führt einen rekursiven Vergleich zwischen zwei Builds durch, wobei häufige Build-bezogene Dateiänderungen ausgeschlossen werden, z
- Erwartete Änderungen in der Build-Ausgabe (z. B. aufgrund einer Änderung der Build-Nummer).
- Änderungen aufgrund bekannter Probleme im aktuellen Build-System.
Führen Sie den folgenden Befehl aus, um das Build-Diff-Tool zu verwenden:
target_files_diff.py dir1 dir2
dir1
und dir2
sind Basisverzeichnisse, die die extrahierten Zieldateien für jeden Build enthalten.
Blockzuweisung konsistent halten
Obwohl der Inhalt einer bestimmten Datei zwischen zwei Builds gleich bleibt, können sich die tatsächlichen Blöcke, die die Daten enthalten, geändert haben. Infolgedessen muss der Updater unnötige E/A ausführen, um die Blöcke für ein OTA-Update zu verschieben.
In einem virtuellen A/B-OTA-Update können unnötige E/A den Speicherplatz erheblich erhöhen, der zum Speichern des Copy-on-Write-Snapshots erforderlich ist. Bei einer Nicht-A/B-OTA-Aktualisierung trägt das Verschieben der Blöcke für eine OTA-Aktualisierung zur Aktualisierungszeit bei, da aufgrund von Blockverschiebungen mehr E/A anfällt.
Um dieses Problem zu beheben, hat Google in Android 7.0 das Tool make_ext4fs erweitert, um die make_ext4fs
über Builds hinweg konsistent zu halten. Das Tool make_ext4fs
akzeptiert ein optionales Flag -d base_fs
, das versucht, Dateien denselben Blöcken zuzuweisen, wenn ein ext4
-Image generiert wird. Sie können die Blockzuordnungsdateien (z. B. die base_fs
Zuordnungsdateien) aus der ZIP-Datei der Zieldateien eines früheren Builds extrahieren. Für jede ext4
-Partition gibt es eine .map
-Datei im Verzeichnis IMAGES
(Beispiel: IMAGES/system.map
entspricht der system
). Diese base_fs
-Dateien können dann wie in diesem Beispiel über PRODUCT_<partition>_BASE_FS_PATH
eingecheckt und angegeben werden:
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
Dies trägt zwar nicht dazu bei, die Gesamtgröße des OTA-Pakets zu reduzieren, verbessert jedoch die OTA-Aktualisierungsleistung, indem die Menge an E/A reduziert wird. Bei virtuellen A/B-Updates wird der für die Anwendung des OTA benötigte Speicherplatz drastisch reduziert.
Vermeiden Sie das Aktualisieren von Apps
Zusätzlich zur Minimierung von Build-Diffs können Sie die Größe von OTA-Updates reduzieren, indem Sie Updates für Apps ausschließen, die Updates über App Stores erhalten. APKs umfassen oft einen erheblichen Teil verschiedener Partitionen auf einem Gerät. Das Einschließen der neuesten Versionen von Apps, die von App Stores in einem OTA-Update aktualisiert werden, kann große Auswirkungen auf OTA-Pakete haben und wenig Nutzen für die Benutzer bieten. Wenn Benutzer ein OTA-Paket erhalten, haben sie möglicherweise bereits die aktualisierte App oder eine noch neuere Version, die sie direkt aus den App Stores erhalten haben.