Reduzierung der OTA-Größe

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 das bsdiff 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 Tools bsdiff , 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:

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:

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:

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:

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.