Reduzieren Sie die OTA-Größe

Auf dieser Seite werden Änderungen beschrieben, 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 für die Reduzierung der Größe ihrer OTA-Updates (Over-the-Air) verwenden.

Android-OTA-Updates enthalten gelegentlich geänderte Dateien, die nicht mit Codeänderungen übereinstimmen. Es handelt sich tatsächlich um Build-System-Artefakte. Dies kann auftreten, 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 Feststellung, welcher Code geändert wurde.

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 außerdem 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 dabei hilft, die Blockzuordnung konsistent zu halten.

Ein Build-System kann auf verschiedene Weise unnötig große Patches erstellen. Um dies zu mildern, 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 Größe der OTA-Update-Pakete reduzierten, gehören die folgenden:

  • Verwendung von Brotli , einem generischen, verlustfreien Komprimierungsalgorithmus für vollständige Bilder bei 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 der Puffin- Rekomprimierung, eines deterministischen Patching-Tools für Deflate-Streams, das die Komprimierungs- und Diff-Funktionen für die A/B-OTA-Update-Generierung übernimmt.
  • Änderungen an der Verwendung des Delta-Generierungstools, 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 dazu, dass bei der Anwendung von Patches für A/B-Geräteaktualisierungen weniger Speicher verbraucht wurde.
  • Verbesserungen beim Aufteilen großer ZIP-Dateien für blockbasierte OTA-Updates. Ein Modus in imgdiff teilt übergroße APK-Dateien basierend auf Eintragsnamen auf. Dadurch entsteht ein kleinerer Patch im Vergleich zur linearen Aufteilung der Dateien und deren Komprimierung mit dem bsdiff Tool.

In den folgenden Abschnitten werden verschiedene Probleme besprochen, 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 diese normalerweise für denselben Checkout dieselbe ist. Werkzeuge wie ls sortieren die Ergebnisse standardmäßig, aber die Platzhalterfunktion, die von Befehlen wie find und make verwendet wird, führt keine Sortierung durch. 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 Kern-Build-System mithilfe des integrierten Makros all-*-files-under behoben, das all-cpp-files-under einschließt (da mehrere Definitionen in anderen Makefiles verteilt waren). Einzelheiten finden Sie hier:

Build-Verzeichnis

Problem: Das Ändern des Verzeichnisses, in dem 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. Allerdings kodieren die Debug-Symbole 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 Debug-Pfade jetzt relativ. Einzelheiten finden Sie in CL: https://android.googlesource.com/platform/build/+/6a66a887baadc9eb3d0d60e26f748b8453e27a02 .

Zeitstempel

Problem: Zeitstempel in der Build-Ausgabe führen zu unnötigen Dateiänderungen. Dies kann an folgenden Orten der Fall sein:

  • __DATE__/__TIME__/__TIMESTAMP__ Makros in C- oder C++-Code.
  • In zip-basierten Archiven eingebettete Zeitstempel.

Lösungen/Beispiele: Um Zeitstempel aus der Build-Ausgabe zu entfernen, verwenden Sie die unten aufgeführten Anweisungen in __DATE__/__TIME__/__TIMESTAMP__ in C/C++. und Eingebettete Zeitstempel in Archiven .

__DATE__/__TIME__/__TIMESTAMP__ in C/C++

Diese Makros erzeugen für unterschiedliche Builds immer unterschiedliche Ausgaben, also verwenden Sie sie nicht. Hier sind einige Möglichkeiten, diese Makros zu entfernen:

Eingebettete Zeitstempel in Archiven (zip, jar)

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

Ein neues Tool, ziptime (zu finden in /platform/build/+/main/tools/ziptime/ ), setzt die normalen Zeitstempel in den Zip-Headern zurück. Einzelheiten finden Sie in der README-Datei .

Das Tool signapk legt Zeitstempel für die APK-Dateien fest, die je nach Serverzeitzone variieren können. Einzelheiten finden Sie im CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028 .

Versionszeichenfolgen

Problem: Bei APK-Versionszeichenfolgen wurde häufig die BUILD_NUMBER an die fest codierten Versionen angehängt. Selbst wenn sich sonst nichts an einem APK ändern würde, wäre das APK dadurch immer noch anders.

Lösung: Entfernen Sie die Build-Nummer aus der APK-Versionszeichenfolge.

Beispiele:

Aktivieren Sie die Wahrheitsberechnung auf dem Gerät

Wenn dm-verity auf Ihrem Gerät aktiviert ist, übernehmen die 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 etwa 16 MB für eine 2-GB-Partition belegen.

Allerdings kann die Berechnung der Wahrheit auf dem Gerät lange dauern. Insbesondere der Forward Error-Correction-Code kann lange dauern. Auf Pixelgeräten dauert es in der Regel bis zu 10 Minuten. Bei 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 beim Generieren eines OTA-Updates --disable_fec_computation an das Tool ota_from_target_files übergeben. Dieses Flag deaktiviert die Wahrheitsberechnung 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 die Übergabe dieses Flags keine Auswirkung.

Konsistente Build-Tools

Problem: Tools, die installierte Dateien generieren, müssen konsistent sein (eine bestimmte Eingabe sollte immer die gleiche Ausgabe erzeugen).

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

Verwenden Sie das Build-Diff-Tool

Für Fälle, in denen es nicht möglich ist, buildbezogene Dateiänderungen zu eliminieren, enthält AOSP ein Build-Diff-Tool, target_files_diff.py , das beim Vergleich zweier Dateipakete verwendet werden kann. Dieses Tool führt einen rekursiven Vergleich zwischen zwei Builds durch und schließt dabei häufige Build-bezogene Dateiänderungen aus, z

  • Erwartete Änderungen in der Build-Ausgabe (z. B. aufgrund einer Änderung der Build-Nummer).
  • Änderungen aufgrund bekannter Probleme im aktuellen Build-System.

Um das Build-Diff-Tool zu verwenden, führen Sie den folgenden Befehl aus:

target_files_diff.py dir1 dir2

dir1 und dir2 sind Basisverzeichnisse, die die extrahierten Zieldateien für jeden Build enthalten.

Halten Sie die Blockzuteilung konsistent

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-Vorgänge ausführen, um die Blöcke für ein OTA-Update zu verschieben.

Bei einem Virtual A/B OTA-Update kann unnötige E/A den zum Speichern des Copy-on-Write-Snapshots erforderlichen Speicherplatz erheblich erhöhen. Bei einem Nicht-A/B-OTA-Update trägt das Verschieben der Blöcke für ein OTA-Update zur Aktualisierungszeit bei, da aufgrund von Blockverschiebungen mehr I/O anfällt.

Um dieses Problem zu beheben, hat Google in Android 7.0 das Tool make_ext4fs erweitert, um die Blockzuteilung über Builds hinweg konsistent zu halten. Das Tool make_ext4fs akzeptiert ein optionales Flag -d base_fs , das versucht, beim Generieren eines ext4 Images Dateien denselben Blöcken zuzuordnen. 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 IMAGES Verzeichnis (beispielsweise entspricht IMAGES/system.map der system ). 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

Dies trägt zwar nicht dazu bei, die Gesamtgröße des OTA-Pakets zu reduzieren, verbessert jedoch die Leistung der OTA-Aktualisierung, 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 die Aktualisierung von Apps

Zusätzlich zur Minimierung von Build-Unterschieden 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 machen oft einen erheblichen Teil verschiedener Partitionen auf einem Gerät aus. Das Einbeziehen der neuesten Versionen von Apps, die von App Stores aktualisiert werden, in ein OTA-Update kann große Auswirkungen auf OTA-Pakete haben und nur geringe Vorteile für den Benutzer bieten. Wenn Benutzer ein OTA-Paket erhalten, verfügen sie möglicherweise bereits über die aktualisierte App oder eine noch neuere Version, die sie direkt aus den App-Stores erhalten haben.