OTA-Größe reduzieren

Auf dieser Seite werden Änderungen beschrieben, die in 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 verwenden, um die Größe ihrer Over-the-Air-Updates (OTA) zu reduzieren.

Android-OTA-Updates enthalten gelegentlich geänderte Dateien, die nicht mit Codeänderungen übereinstimmen. Tatsächlich handelt es sich um Artefakte des Build-Systems. Dies kann passieren, 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 zusätzlichen 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 Änderungen am Build-System, die darauf ausgelegt sind, die Größe von OTA-Patches zu reduzieren. Unnötige Dateiänderungen zwischen Builds wurden eliminiert und OTA-Updates enthalten nur Patch-bezogene Dateien. AOSP enthält außerdem ein Tool zum Vergleichen von Builds, das häufige buildbezogene Dateiänderungen herausfiltert, um einen übersichtlicheren Vergleich von Build-Dateien zu ermöglichen, sowie ein Tool zum Zuordnen von Blöcken, mit dem Sie die Blockzuweisung konsistent halten können.

Ein Build-System kann auf verschiedene Weise unnötig große Patches erstellen. Um dies zu vermeiden, wurden in Android 8.0 und höher neue Funktionen implementiert, um die Patchgröße für jeden Dateidiff zu reduzieren. Die folgenden Verbesserungen haben dazu beigetragen, die Größe von OTA-Updatepaketen zu reduzieren:

  • Verwendung von ZSTD, einem universellen, verlustfreien Komprimierungsalgorithmus für vollständige Images bei Updates auf Nicht-A/B-Geräten. ZSTD kann für höhere Komprimierungsverhältnisse angepasst werden, indem die Komprimierungsstufe erhöht wird. Die Komprimierungsstufe wird während der OTA-Generierung festgelegt. Sie kann durch Übergabe des Flags --vabc_compression_param=zstd,$COMPRESSION_LEVEL festgelegt werden.
  • Die Größe des Komprimierungsfensters, das während der OTA-Aktualisierung verwendet wird, wird erhöht. Die maximale Größe des Komprimierungsfensters kann durch Anpassen des Build-Parameters in der .mk-Datei eines Geräts festgelegt werden. Diese Variable wird als PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 262144 festgelegt.
  • Verwendung der Puffin-Rekompression, einem deterministischen Patching-Tool für Deflate-Streams, das die Komprimierungs- und Differenzfunktionen für die A/B-OTA-Update-Generierung übernimmt.
  • Änderungen bei der Verwendung des Tools zur Deltagenerierung, z. B. wie die Bibliothek bsdiff zum Komprimieren von Patches verwendet wird. Unter Android 9 und höher wählt das Tool bsdiff den Komprimierungsalgorithmus aus, der die besten Komprimierungsergebnisse für einen Patch liefert.
  • Durch Verbesserungen an update_engine wird weniger Arbeitsspeicher belegt, wenn Patches für A/B-Geräteupdates angewendet werden.

In den folgenden Abschnitten werden verschiedene Probleme behandelt, die sich auf die Größe von OTA-Updates auswirken, sowie Lösungen und Beispiele für die Implementierung in AOSP.

Reihenfolge der Dateien

Problem: Dateisysteme garantieren keine bestimmte Reihenfolge, wenn eine Liste von Dateien in einem Verzeichnis angefordert wird. Bei demselben Checkout ist die Reihenfolge jedoch in der Regel gleich. Tools wie ls sortieren die Ergebnisse standardmäßig, aber die Platzhalterfunktion, 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 sie ebenfalls. Einige Tools, z. B. Java, sortieren Eingaben. Prüfen Sie daher, ob das von Ihnen verwendete Tool die Dateien bereits sortiert hat, bevor Sie sie sortieren.

Beispiele:Viele Instanzen wurden im Core-Build-System mit dem integrierten Makro all-*-files-under korrigiert, das all-cpp-files-under enthält (da mehrere Definitionen auf andere Makefiles verteilt waren). Weitere Informationen finden Sie unter:

Build-Verzeichnis

Problem:Wenn Sie das Verzeichnis ändern, in dem Elemente erstellt werden, können sich die Binärdateien unterscheiden. Die meisten Pfade im Android-Build sind relative Pfade, sodass __FILE__ in C/C++ kein Problem darstellt. In den Debug-Symbolen wird jedoch standardmäßig der vollständige Pfadname codiert. Die .note.gnu.build-id wird durch Hashing des vorab bereinigten Binärcodes generiert. Sie ändert sich also, wenn sich die Debug-Symbole ändern.

Lösung:In AOSP sind Debug-Pfade jetzt relativ. Weitere Informationen 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 wird wahrscheinlich an den folgenden Orten passieren:

  • __DATE__/__TIME__/__TIMESTAMP__-Makros in C- oder C++-Code.
  • Zeitstempel, die in ZIP-basierten Archiven eingebettet sind.

Lösungen/Beispiele:Wenn Sie Zeitstempel aus der Build-Ausgabe entfernen möchten, folgen Sie der Anleitung unten unter __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 daher nicht. Hier sind einige Optionen zum Entfernen dieser Makros:

Eingebettete Zeitstempel in Archiven (ZIP, JAR)

Unter Android 7.0 wurde das Problem mit eingebetteten Zeitstempeln in ZIP-Archiven behoben, indem -X bei allen Verwendungen des Befehls zip hinzugefügt wurde. Dadurch wurden die UID/GID des Builders und der erweiterte Unix-Zeitstempel aus der ZIP-Datei entfernt.

Ein neues Tool, ziptime (im Verzeichnis /platform/build/+/android16-release/tools/ziptime/), setzt die normalen Zeitstempel in den ZIP-Headern zurück. Weitere Informationen finden Sie in der README-Datei.

Mit dem Tool signapk werden Zeitstempel für die APK-Dateien festgelegt, die je nach Serverzeitzone variieren können. Weitere Informationen finden Sie im CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028.

Mit dem Tool signapk werden Zeitstempel für die APK-Dateien festgelegt, die je nach Serverzeitzone variieren können. Weitere Informationen finden Sie im CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028.

Versionsstrings

Problem:An die hartcodierten Versionen von APK-Versionsstrings wurde oft BUILD_NUMBER angehängt. Auch wenn sich in einem APK nichts anderes geändert hat, wäre es trotzdem anders.

Lösung:Entfernen Sie die Build-Nummer aus dem APK-Versionsstring.

Beispiele:

Verity-Berechnung auf dem Gerät aktivieren

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. So 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 für eine 2-GB-Partition etwa 16 MB verwenden.

Die Berechnung der Wahrhaftigkeit auf dem Gerät kann jedoch lange dauern. Insbesondere kann der Forward Error-Correction-Code 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 Berechnung von „on-device verity“ deaktivieren, aber „dm-verity“ weiterhin aktivieren möchten, können Sie dazu --disable_fec_computation an das Tool ota_from_target_files übergeben, wenn Sie ein OTA-Update generieren. Mit diesem Flag wird die Berechnung von „Verity“ auf dem Gerät während OTA-Updates deaktiviert. Dadurch wird die OTA-Installationszeit verkürzt, aber die OTA-Paketgröße erhöht. Wenn auf Ihrem Gerät „dm-verity“ nicht aktiviert ist, hat das Übergeben dieses Flags keine Auswirkungen.

Konsistente Build-Tools

Problem:Tools, mit denen installierte Dateien generiert werden, müssen konsistent sein. Eine bestimmte Eingabe sollte immer dieselbe Ausgabe erzeugen.

Lösungen/Beispiele:In den folgenden Build-Tools waren Änderungen erforderlich:

Build-Vergleichstool verwenden

Für Fälle, in denen es nicht möglich ist, buildbezogene Dateiänderungen zu vermeiden, enthält AOSP ein Tool zum Vergleichen von Builds, target_files_diff.py, mit dem zwei Dateipakete verglichen werden können. Dieses Tool führt einen rekursiven Vergleich zwischen zwei Builds durch und schließt dabei allgemeine buildbezogene Dateiänderungen aus, z. B.

  • 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 Tool zum Vergleichen von Builds 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, in denen die Daten gespeichert sind, geändert haben. Daher muss das Updater-Tool unnötige Ein-/Ausgabeoperationen ausführen, um die Blöcke für ein OTA-Update zu verschieben.

Bei einem virtuellen A/B-OTA-Update kann unnötige E/A den Speicherplatz, der zum Speichern des Copy-on-Write-Snapshots erforderlich ist, erheblich erhöhen. Bei einem OTA-Update ohne A/B-Partitionen trägt das Verschieben der Blöcke für ein OTA-Update zur Aktualisierungszeit bei, da aufgrund der Blockverschiebungen mehr E/A-Vorgänge erforderlich sind.

Um dieses Problem zu beheben, hat Google in Android 7.0 das Tool make_ext4fs erweitert, um die Blockzuweisung über verschiedene Builds hinweg konsistent zu halten. Das make_ext4fs-Tool akzeptiert das optionale Flag -d base_fs, mit dem versucht wird, Dateien beim Generieren eines ext4-Images denselben Blöcken zuzuweisen. Sie können die Blockzuordnungsdateien (z. B. die base_fs-Kartendateien) aus der ZIP-Datei der Zieldateien eines vorherigen Builds extrahieren. Für jede ext4-Partition gibt es eine .map-Datei im Verzeichnis IMAGES (z. B. entspricht IMAGES/system.map der system-Partition). Diese base_fs-Dateien können dann eingecheckt und über PRODUCT_<partition>_BASE_FS_PATH angegeben werden, wie in diesem Beispiel:

  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

Dadurch wird zwar nicht die Gesamtgröße des OTA-Pakets reduziert, aber die Leistung von OTA-Updates wird durch die Reduzierung der E/A-Menge verbessert. Bei virtuellen A/B-Updates wird der für die Anwendung des OTA-Updates benötigte Speicherplatz drastisch reduziert.

App-Updates vermeiden

Neben der Minimierung von Build-Unterschieden können Sie die Größe von OTA-Updates reduzieren, indem Sie Updates für Apps ausschließen, die über App-Shops aktualisiert werden. APKs machen oft einen erheblichen Teil verschiedener Partitionen auf einem Gerät aus. Die Einbeziehung der neuesten Versionen von Apps, die von App-Shops aktualisiert werden, in ein OTA-Update kann sich stark auf die Größe von OTA-Paketen auswirken und bietet nur wenig Vorteile für Nutzer. Wenn Nutzer ein OTA-Paket erhalten, haben sie möglicherweise bereits die aktualisierte App oder eine noch neuere Version, die sie direkt aus App-Shops erhalten haben.