APEX-Dateiformat

Das Containerformat Android Pony EXpress (APEX) wurde in Android 10 eingeführt und wird bei der Installation von Systemmodulen der unteren Ebene verwendet. Dieses Format erleichtert das Aktualisieren von Systemkomponenten, die nicht in das Standard-Android-Anwendungsmodell passen. Einige Beispielkomponenten sind native Dienste und Bibliotheken, Hardwareabstraktionsebenen (HALs), Laufzeit (ART) und Klassenbibliotheken.

Der Begriff „APEX“ kann sich auch auf eine APEX-Datei beziehen.

Hintergrund

Android unterstützt zwar Updates von Modulen, die in das Standard-App-Modell passen (z. B. Dienste, Aktivitäten), über Paketinstallations-Apps (z. B. die Google Play Store App), aber die Verwendung eines ähnlichen Modells für untergeordnete Betriebssystemkomponenten hat folgende Nachteile:

  • APK-basierte Module können nicht zu Beginn der Bootsequenz verwendet werden. Der Paketmanager ist das zentrale Repository mit Informationen zu Anwendungen und kann nur über den Aktivitätsmanager gestartet werden, der in einer späteren Phase des Bootvorgangs zur Verfügung steht.
  • Das APK-Format, insbesondere das Manifest, ist für Android-Apps konzipiert. Systemmodule sind nicht immer geeignet.

Design

In diesem Abschnitt wird das allgemeine Design des APEX-Dateiformats und des APEX-Managers beschrieben, einem Dienst zur Verwaltung von APEX-Dateien.

Weitere Informationen dazu, warum dieses Design für APEX ausgewählt wurde, finden Sie unter Bei der Entwicklung von APEX berücksichtigte Alternativen.

APEX-Format

Dies ist das Format einer APEX-Datei.

APEX-Dateiformat

Abbildung 1. APEX-Dateiformat

Auf der obersten Ebene ist eine APEX-Datei eine ZIP-Datei, in der Dateien unkomprimiert und an 4-KB-Grenzen gespeichert werden.

Die vier Dateien in einer APEX-Datei sind:

  • apex_manifest.json
  • AndroidManifest.xml
  • apex_payload.img
  • apex_pubkey

Die Datei apex_manifest.json enthält den Paketnamen und die Version, die eine APEX-Datei identifizieren. Dies ist ein ApexManifest-Protokollpuffer im JSON-Format.

Die AndroidManifest.xml-Datei ermöglicht es der APEX-Datei, APK-bezogene Tools und Infrastrukturen wie ADB, PackageManager und Paketinstallations-Apps (z. B. Play Store) zu verwenden. Für die APEX-Datei können Sie beispielsweise ein vorhandenes Tool wie aapt verwenden, um grundlegende Metadaten aus der Datei zu prüfen. Die Datei enthält den Paketnamen und die Version. Diese Informationen sind in der Regel auch in apex_manifest.json verfügbar.

Für neuen Code und Systeme, die mit APEX zu tun haben, wird apex_manifest.json anstelle von AndroidManifest.xml empfohlen. AndroidManifest.xml kann zusätzliche Informationen zum Targeting enthalten, die von den vorhandenen Tools zum App-Veröffentlichen verwendet werden können.

apex_payload.img ist ein ext4-Dateisystem-Image, das von dm-verity unterstützt wird. Das Image wird zur Laufzeit über ein Loopback-Gerät bereitgestellt. Insbesondere werden der Hash-Baum und der Metadatenblock mit der libavb-Bibliothek erstellt. Die Nutzlast des Dateisystems wird nicht geparst, weil das Image an Ort und Stelle bereitgestellt werden kann. Reguläre Dateien sind in der Datei apex_payload.img enthalten.

apex_pubkey ist der öffentliche Schlüssel, mit dem das Dateisystem-Image signiert wird. Zur Laufzeit sorgt dieser Schlüssel dafür, dass das heruntergeladene APEX mit derselben Entität signiert ist, die denselben APEX in den integrierten Partitionen signiert.

APEX-Benennungsrichtlinien

Beachten Sie die folgenden Benennungsrichtlinien, um Namenskonflikte zwischen neuen APEX-Objekten bei der Weiterentwicklung der Plattform zu vermeiden:

  • com.android.*
    • Reserviert für AOSP-APEXes. Sie ist für kein Unternehmen oder Gerät einzigartig.
  • com.<companyname>.*
    • Für ein Unternehmen reserviert. Wird möglicherweise von mehreren Geräten dieses Unternehmens verwendet.
  • com.<companyname>.<devicename>.*
    • Reserviert für APEX, die für ein bestimmtes Gerät (oder eine Untergruppe von Geräten) eindeutig sind.

APEX-Manager

Der APEX-Manager (apexd) ist ein eigenständiger nativer Prozess, der für die Überprüfung, Installation und Deinstallation von APEX-Dateien verantwortlich ist. Dieser Prozess wird gestartet und steht früh in der Bootsequenz bereit. APEX-Dateien sind normalerweise unter /system/apex auf dem Gerät vorinstalliert. Der APEX-Manager verwendet diese Pakete standardmäßig, wenn keine Updates verfügbar sind.

Für die Aktualisierungssequenz eines APEX wird die Klasse "PackageManager" verwendet. Sie sieht so aus:

  1. Eine APEX-Datei wird über eine Paketinstallations-App, ADB oder eine andere Quelle heruntergeladen.
  2. Der Paketmanager startet den Installationsvorgang. Wenn der Paketmanager erkennt, dass es sich um eine APEX-Datei handelt, übergibt er die Kontrolle an den APEX-Manager.
  3. Der APEX-Manager überprüft die APEX-Datei.
  4. Wenn die APEX-Datei bestätigt wurde, wird die interne Datenbank des APEX-Managers so aktualisiert, dass die APEX-Datei beim nächsten Start aktiviert wird.
  5. Der Installationsanforderer empfängt nach erfolgreicher Paketüberprüfung eine Nachricht.
  6. Um die Installation fortzusetzen, muss das System neu gestartet werden.
  7. Beim nächsten Start wird der APEX-Manager gestartet, die interne Datenbank gelesen und für jede aufgeführte APEX-Datei Folgendes ausgeführt:

    1. Prüft die APEX-Datei.
    2. Erstellt ein Loopback-Gerät aus der APEX-Datei.
    3. Erstellt ein Device Mapper-Blockgerät über dem Loopback-Gerät.
    4. Hiermit wird das Blockgerät des Gerätemappers auf einem eindeutigen Pfad bereitgestellt (z. B. /apex/name@ver).

Wenn alle in der internen Datenbank aufgeführten APEX-Dateien bereitgestellt sind, stellt der APEX-Manager einen Binder-Dienst für andere Systemkomponenten bereit, um Informationen zu den installierten APEX-Dateien abzufragen. Die anderen Systemkomponenten können beispielsweise die Liste der auf dem Gerät installierten APEX-Dateien oder den genauen Pfad abfragen, unter dem ein bestimmtes APEX bereitgestellt ist, damit auf die Dateien zugegriffen werden kann.

APEX-Dateien sind APK-Dateien

APEX-Dateien sind gültige APK-Dateien, da sie signierte ZIP-Archive (mit dem APK-Signaturschema) sind, die eine AndroidManifest.xml-Datei enthalten. So können APEX-Dateien die Infrastruktur für APK-Dateien verwenden, z. B. eine Paketinstallations-App, das Signaturdienstprogramm und den Paketmanager.

Die AndroidManifest.xml-Datei in einer APEX-Datei ist minimal und besteht aus dem Paket name, versionCode und optional targetSdkVersion, minSdkVersion und maxSdkVersion für ein detailliertes Targeting. Mit diesen Informationen können APEX-Dateien über vorhandene Kanäle wie Paketinstallationsanwendungen und ADB bereitgestellt werden.

Unterstützte Dateitypen

Das APEX-Format unterstützt die folgenden Dateitypen:

  • Native freigegebene Bibliotheken
  • Native ausführbare Dateien
  • JAR-Dateien
  • Datendateien
  • Konfigurationsdateien

Das bedeutet nicht, dass APEX alle diese Dateitypen aktualisieren kann. Ob ein Dateityp aktualisiert werden kann, hängt von der Plattform ab und davon, wie stabil die Definitionen der Schnittstellen für die Dateitypen sind.

Signaturoptionen

APEX-Dateien werden auf zwei Arten signiert. Zuerst wird die Datei apex_payload.img (insbesondere der an apex_payload.img angehängte vbmeta-Deskriptor) mit einem Schlüssel signiert. Anschließend wird die gesamte APEX mit dem APK-Signaturschema v3 signiert. Dabei werden zwei verschiedene Schlüssel verwendet.

Auf Geräteseite wird ein öffentlicher Schlüssel installiert, der dem privaten Schlüssel entspricht, der zum Signieren des vbmeta-Deskriptors verwendet wurde. Der APEX-Manager prüft mit dem öffentlichen Schlüssel die APEX-Dateien, die zur Installation angefordert werden. Jedes APEX muss mit verschiedenen Schlüsseln signiert sein und wird sowohl zum Build-Zeitpunkt als auch zur Laufzeit erzwungen.

APEX in integrierten Partitionen

APEX-Dateien können sich in integrierten Partitionen wie /system befinden. Die Partition ist bereits über dm-verity, sodass die APEX-Dateien direkt über das Loopback-Gerät bereitgestellt werden.

Wenn in einer integrierten Partition ein APEX vorhanden ist, kann das APEX aktualisiert werden, indem ein APEX-Paket mit demselben Paketnamen und einem Versionscode größer oder gleich Version bereitgestellt wird. Die neue APEX-Datei wird in /data gespeichert und ähnlich wie bei APKs wird die Version, die sich bereits in der integrierten Partition befindet, von der neu installierten Version überschattet. Im Gegensatz zu APKs wird die neu installierte Version von APEX jedoch erst nach einem Neustart aktiviert.

Kernelanforderungen

Zur Unterstützung von APEX-Mainline-Modulen auf einem Android-Gerät sind die folgenden Linux-Kernel-Funktionen erforderlich: der Loopback-Treiber und dm-verity. Der Loopback-Treiber stellt das Dateisystem-Image in einem APEX-Modul bereit und dm-verity überprüft das APEX-Modul.

Die Leistung des Loopback-Treibers und des dm-verity ist wichtig, um bei der Verwendung von APEX-Modulen eine gute Systemleistung zu erzielen.

Unterstützte Kernel-Versionen

APEX-Mainline-Module werden auf Geräten mit Kernel-Versionen ab 4.4 unterstützt. Neue Geräte, die mit Android 10 oder höher auf den Markt gebracht werden, müssen die Kernelversion 4.9 oder höher verwenden, um APEX-Module zu unterstützen.

Erforderliche Kernel-Patches

Die erforderlichen Kernel-Patches zur Unterstützung von APEX-Modulen sind in der allgemeinen Android-Baumstruktur enthalten. Verwenden Sie die neueste Version des Android Common Tree, um die Patches für die APEX-Unterstützung zu erhalten.

Kernel-Version 4.4

Diese Version wird nur für Geräte unterstützt, die von Android 9 auf Android 10 umgestellt werden und APEX-Module unterstützen sollen. Zum Abrufen der erforderlichen Patches wird dringend eine Down-Merge-Funktion vom Zweig android-4.4 empfohlen. Im Folgenden finden Sie eine Liste der erforderlichen einzelnen Patches für Kernel-Version 4.4.

  • UPSTREAM: Schleife: ioctl zum Ändern der logischen Blockgröße hinzufügen (4.4)
  • BACKPORT: block/loop: set hw_sectors (4.4)
  • UPSTREAM: Schleife: LOOP_SET_BLOCK_SIZE in kompatibler iOS-App hinzufügen (4.4)
  • ANDROID: mnt: Problem mit next_descendent beheben (4.4)
  • ANDROID: mnt: remount sollte für Slaves of Slaves weitergegeben werden (4.4)
  • ANDROID: mnt: Remount korrekt weitergeben (4.4)
  • "ANDROID: dm verity: Mindest-Prefetch-Größe hinzufügen" wiederherstellen (4.4)
  • UPSTREAM: Schleife: Caches werden gelöscht, wenn Offset oder block_size geändert werden (4.4)

Kernel-Versionen 4.9/4.14/4.19

Führen Sie eine Down-Zusammenführung vom android-common-Zweig aus, um die erforderlichen Patches für die Kernel-Versionen 4.9/4.14/4.19 zu erhalten.

Erforderliche Kernelkonfigurationsoptionen

In der folgenden Liste sind die grundlegenden Konfigurationsanforderungen für die Unterstützung von APEX-Modulen aufgeführt, die in Android 10 eingeführt wurden. Die Elemente mit einem Sternchen (*) sind bestehende Anforderungen von Android 9 und niedriger.

(*) CONFIG_AIO=Y # AIO support (for direct I/O on loop devices)
CONFIG_BLK_DEV_LOOP=Y # for loop device support
CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 # pre-create 16 loop devices
(*) CONFIG_CRYPTO_SHA1=Y # SHA1 hash for DM-verity
(*) CONFIG_CRYPTO_SHA256=Y # SHA256 hash for DM-verity
CONFIG_DM_VERITY=Y # DM-verity support

Anforderungen an Kernel-Befehlszeilenparameter

Zur Unterstützung von APEX müssen die Kernel-Befehlszeilenparameter die folgenden Anforderungen erfüllen:

  • loop.max_loop darf NICHT festgelegt werden
  • loop.max_part muss kleiner oder gleich 8 sein

APEX erstellen

In diesem Abschnitt wird beschrieben, wie Sie ein APEX mit dem Android-Build-System erstellen. Das folgende Beispiel zeigt Android.bp für eine APEX mit dem Namen apex.test.

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    // libc.so and libcutils.so are included in the apex
    native_shared_libs: ["libc", "libcutils"],
    binaries: ["vold"],
    java_libs: ["core-all"],
    prebuilts: ["my_prebuilt"],
    compile_multilib: "both",
    key: "apex.test.key",
    certificate: "platform",
}

Beispiel für apex_manifest.json:

{
  "name": "com.android.example.apex",
  "version": 1
}

file_contexts Beispiel:

(/.*)?           u:object_r:system_file:s0
/sub(/.*)?       u:object_r:sub_file:s0
/sub/file3       u:object_r:file3_file:s0

Dateitypen und Speicherorte in APEX

Dateityp Speicherort in APEX
Geteilte Fotogalerien /lib und /lib64 (/lib/arm für übersetzte ARM-Code in x86)
Ausführbare Dateien /bin
Java-Bibliotheken /javalib
Vorgefertigt /etc

Transitive Abhängigkeiten

APEX-Dateien enthalten automatisch transitive Abhängigkeiten von nativen freigegebenen Bibliotheken oder ausführbaren Dateien. Wenn libFoo beispielsweise von libBar abhängt, werden die beiden Bibliotheken eingeschlossen, wenn im Attribut native_shared_libs nur libFoo aufgeführt ist.

Mehrere ABIs verarbeiten

Installieren Sie die native_shared_libs-Eigenschaft sowohl für die primäre als auch für die sekundäre Binärschnittstelle (Application Binary Interface, ABI) des Geräts. Wenn ein APEX auf Geräte mit einer einzelnen ABI ausgerichtet ist (d. h. nur 32 Bit oder nur 64 Bit), werden nur Bibliotheken mit der entsprechenden ABI installiert.

Installieren Sie die Property binaries nur für das primäre ABI des Geräts, wie unten beschrieben:

  • Wenn das Gerät nur 32 Bit unterstützt, wird nur die 32-Bit-Variante des Binärprogramms installiert.
  • Wenn das Gerät nur 64-Bit unterstützt, wird nur die 64-Bit-Variante des Binärprogramms installiert.

Wenn Sie die ABIs der nativen Bibliotheken und Binärdateien genau steuern möchten, verwenden Sie die multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]-Eigenschaften.

  • first: Muss mit der primären ABI des Geräts übereinstimmen. Dies ist die Standardeinstellung für Binärdateien.
  • lib32: entspricht der 32-Bit-ABI des Geräts, falls unterstützt.
  • lib64: entspricht der 64-Bit-ABI des Geräts, die unterstützt wird.
  • prefer32: Entspricht dem 32-Bit-ABI des Geräts, sofern unterstützt. Wenn das 32-Bit-ABI nicht unterstützt wird, entspricht es dem 64-Bit-ABI.
  • both: Stimmt mit beiden ABIs überein. Das ist die Standardeinstellung für native_shared_libraries.

Die Attribute java, libraries und prebuilts sind ABI-unabhängig.

Das folgende Beispiel bezieht sich auf ein Gerät, das 32/64 unterstützt, aber nicht 32 bevorzugt:

apex {
    // other properties are omitted
    native_shared_libs: ["libFoo"], // installed for 32 and 64
    binaries: ["exec1"], // installed for 64, but not for 32
    multilib: {
        first: {
            native_shared_libs: ["libBar"], // installed for 64, but not for 32
            binaries: ["exec2"], // same as binaries without multilib.first
        },
        both: {
            native_shared_libs: ["libBaz"], // same as native_shared_libs without multilib
            binaries: ["exec3"], // installed for 32 and 64
        },
        prefer32: {
            native_shared_libs: ["libX"], // installed for 32, but not for 64
        },
        lib64: {
            native_shared_libs: ["libY"], // installed for 64, but not for 32
        },
    },
}

vbmeta-Signatur

Signieren Sie jedes APEX mit verschiedenen Schlüsseln. Wenn ein neuer Schlüssel erforderlich ist, erstellen Sie ein Paar aus öffentlichem und privatem Schlüssel und ein apex_key-Modul. Verwenden Sie die Property key, um die APEX-Datei mit dem Schlüssel zu signieren. Der öffentliche Schlüssel ist automatisch in Apex mit dem Namen avb_pubkey enthalten.

# create an rsa key pair
openssl genrsa -out foo.pem 4096

# extract the public key from the key pair
avbtool extract_public_key --key foo.pem --output foo.avbpubkey

# in Android.bp
apex_key {
    name: "apex.test.key",
    public_key: "foo.avbpubkey",
    private_key: "foo.pem",
}

Im obigen Beispiel wird der Name des öffentlichen Schlüssels (foo) zur ID des Schlüssels. Die ID des Schlüssels, mit dem eine APEX-Datei signiert wurde, wird in die APEX-Datei geschrieben. Zur Laufzeit überprüft apexd das APEX anhand eines öffentlichen Schlüssels mit derselben ID im Gerät.

APEX-Signatur

Signieren Sie APEX-Dateien auf die gleiche Weise wie APKs. Signieren Sie APEX zweimal: einmal für das Minidateisystem (apex_payload.img-Datei) und einmal für die gesamte Datei.

Wenn Sie eine APEX-Datei auf Dateiebene signieren möchten, können Sie das Attribut certificate auf eine der folgenden drei Arten festlegen:

  • Nicht festgelegt: Wenn kein Wert festgelegt ist, wird die APEX mit dem Zertifikat unter PRODUCT_DEFAULT_DEV_CERTIFICATE signiert. Wenn kein Flag festgelegt ist, wird der Pfad standardmäßig auf build/target/product/security/testkey gesetzt.
  • <name>: APEX ist mit dem Zertifikat <name> im selben Verzeichnis wie PRODUCT_DEFAULT_DEV_CERTIFICATE signiert.
  • :<name>: Das APEX ist mit dem Zertifikat signiert, das vom Soong-Modul <name> definiert wird. Das Zertifikatsmodul kann so definiert werden:
android_app_certificate {
    name: "my_key_name",
    certificate: "dir/cert",
    // this will use dir/cert.x509.pem (the cert) and dir/cert.pk8 (the private key)
}

APEX installieren

Verwenden Sie ADB, um ein APEX zu installieren.

adb install apex_file_name
adb reboot

Wenn supportsRebootlessUpdate in apex_manifest.json auf true gesetzt ist und das aktuell installierte APEX nicht verwendet wird (z. B. wurden alle darin enthaltenen Dienste beendet), kann ein neues APEX ohne Neustart mit dem Flag --force-non-staged installiert werden.

adb install --force-non-staged apex_file_name

APEX verwenden

Nach dem Neustart wird APEX im Verzeichnis /apex/<apex_name>@<version> bereitgestellt. Es können mehrere Versionen derselben APEX gleichzeitig bereitgestellt werden. Unter den Bereitstellungspfaden wird derjenige, der der neuesten Version entspricht, unter /apex/<apex_name> per Bindung bereitgestellt.

Clients können den über Bindung bereitgestellten Pfad verwenden, um Dateien aus APEX zu lesen oder auszuführen.

APTEX werden in der Regel so verwendet:

  1. Ein OEM oder ODM lädt beim Versand des Geräts eine APEX unter /system/apex vor.
  2. Auf Dateien im APEX wird über den Pfad /apex/<apex_name>/ zugegriffen.
  3. Wenn eine aktualisierte Version des APEX in /data/apex installiert wird, verweist der Pfad nach dem Neustart auf das neue APEX.

Dienst mit einem APEX aktualisieren

So aktualisieren Sie einen Dienst mit einem APEX:

  1. Markieren Sie den Dienst in der Systempartition als aktualisierbar. Fügen Sie der Dienstdefinition die Option updatable hinzu.

    /system/etc/init/myservice.rc:
    
    service myservice /system/bin/myservice
        class core
        user system
        ...
        updatable
    
  2. Erstellen Sie eine neue .rc-Datei für den aktualisierten Dienst. Verwenden Sie die Option override, um den vorhandenen Dienst neu zu definieren.

    /apex/my.apex/etc/init.rc:
    
    service myservice /apex/my.apex/bin/myservice
        class core
        user system
        ...
        override
    

Dienstdefinitionen können nur in der .rc-Datei einer APEX-Anwendung definiert werden. Aktionstrigger werden in APEXes nicht unterstützt.

Wenn ein als aktualisierbar gekennzeichneter Dienst gestartet wird, bevor die APEX-Objekte aktiviert wurden, wird der Start verzögert, bis die APEX-Objekte aktiviert wurden.

System für APEX-Updates konfigurieren

Legen Sie die folgende Systemeigenschaft auf true fest, um APEX-Dateiupdates zu unterstützen.

<device.mk>:

PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true

BoardConfig.mk:
TARGET_FLATTEN_APEX := false

oder einfach

<device.mk>:

$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)

Abgeflachte APEX-Datei

Bei älteren Geräten ist es manchmal unmöglich oder nicht möglich, den alten Kernel so zu aktualisieren, dass er APEX vollständig unterstützt. Der Kernel wurde möglicherweise ohne CONFIG_BLK_DEV_LOOP=Y erstellt, was für die Bereitstellung des Dateisystem-Images in einem APEX entscheidend ist.

Flattened APEX ist ein speziell entwickeltes APEX, das auf Geräten mit einem Legacy-Kernel aktiviert werden kann. Dateien in einer flachen APEX-Datei werden direkt in einem Verzeichnis unter der integrierten Partition installiert. Beispiel: lib/libFoo.so in einer abgeflachen APEX-my.apex-Datei wird in /system/apex/my.apex/lib/libFoo.so installiert.

Beim Aktivieren eines Flattened APEX wird das Loop Device nicht verwendet. Das gesamte Verzeichnis /system/apex/my.apex wird direkt durch Bindung an /apex/name@ver bereitgestellt.

Vereinfachte APEX-Dateien können nicht durch Herunterladen aktualisierter Versionen der APEX-Dateien aus dem Netzwerk aktualisiert werden, da die heruntergeladenen APEXes nicht vereinfacht werden können. Flattened APEXes können nur über eine reguläre OTA aktualisiert werden.

Das vereinfachte APEX ist die Standardkonfiguration. Das bedeutet, dass alle APEX-Dateien standardmäßig flach sind, es sei denn, Sie konfigurieren Ihr Gerät explizit so, dass nicht flache APEX-Dateien erstellt werden, um APEX-Updates zu unterstützen (wie oben beschrieben).

Das Mischen von vereinfachten und nicht vereinfachten APEXes in einem Gerät wird NICHT unterstützt. APEX in einem Gerät müssen entweder alle nicht oder nur reduziert sein. Das ist besonders wichtig, wenn vorsignierte APEX-Prebuilts für Projekte wie Mainline bereitgestellt werden. Nicht vorsignierte APEX-Dateien, d. h., die aus der Quelle erstellt wurden, sollten ebenfalls nicht vereinfacht und mit richtigen Schlüsseln signiert sein. Das Gerät sollte die Einstellungen von updatable_apex.mk übernehmen, wie unter Dienst mit einem APEX aktualisieren beschrieben.

Komprimierte APEX-Dateien

Android 12 und höher bieten die APEX-Komprimierung, um die Auswirkungen aktualisierbarer APEX-Pakete auf den Speicher zu reduzieren. Nachdem ein Update auf ein ApEX installiert wurde, belegt es weiterhin den gleichen Speicherplatz, auch wenn die vorinstallierte Version nicht mehr verwendet wird. Dieser belegte Speicherplatz ist weiterhin nicht verfügbar.

Die APEX-Komprimierung minimiert diese Speicherbelastung durch die Verwendung eines stark komprimierten Satzes von APEX-Dateien auf schreibgeschützten Partitionen (z. B. der Partition /system). In Android 12 und höher wird der ZIP-Komprimierungsalgorithmus DEFLATE verwendet.

Die Komprimierung bietet keine Optimierung für Folgendes:

  • Bootstrap-APEXes, die sehr früh in der Bootreihenfolge bereitgestellt werden müssen.

  • Nicht aktualisierbare APEXe. Die Komprimierung ist nur dann von Vorteil, wenn auf der /data-Partition eine aktualisierte Version eines APEX installiert ist. Eine vollständige Liste der aktualisierbaren APEXes finden Sie auf der Seite Modulare Systemkomponenten.

  • Dynamic Shared Libs APEXes. Da apexd immer beide Versionen solcher APEX (vorinstalliert und aktualisiert) aktiviert, bietet die Komprimierung keinen Mehrwert.

Komprimiertes APEX-Dateiformat

Das ist das Format einer komprimierten APEX-Datei.

Das Diagramm zeigt das Format einer komprimierten APEX-Datei.

Abbildung 2: Komprimiertes APEX-Dateiformat

Auf der obersten Ebene ist eine komprimierte APEX-Datei eine ZIP-Datei, die die ursprüngliche Apex-Datei im dekomprimierten Format und mit der Komprimierungsstufe 9 enthält. Andere Dateien werden unkomprimiert gespeichert.

Eine APEX-Datei besteht aus vier Dateien:

  • original_apex: entleert mit der Komprimierungsstufe 9 Das ist die nicht komprimierte Originaldatei APEX.
  • apex_manifest.pb: nur gespeichert
  • AndroidManifest.xml: nur gespeichert
  • apex_pubkey: nur gespeichert

Die Dateien apex_manifest.pb, AndroidManifest.xml und apex_pubkey sind Kopien der entsprechenden Dateien in original_apex.

Komprimiertes APEX erstellen

Komprimiertes APEX kann mit dem apex_compression_tool.py-Tool unter system/apex/tools erstellt werden.

Im Build-System sind mehrere Parameter im Zusammenhang mit der APEX-Komprimierung verfügbar.

In Android.bp wird die Komprimierbarkeit einer APEX-Datei über das Attribut compressible gesteuert:

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    compressible: true,
}

Ein PRODUCT_COMPRESSED_APEX-Produkt-Flag steuert, ob ein aus der Quelle erstelltes System-Image komprimierte APEX-Dateien enthalten muss.

Bei lokalen Tests können Sie APEX-Dateien bei der Erstellung erzwingen, indem Sie OVERRIDE_PRODUCT_COMPRESSED_APEX= auf true setzen.

Komprimierte APEX-Dateien, die vom Build-System generiert wurden, haben die Erweiterung .capex. Die Erweiterung erleichtert die Unterscheidung zwischen komprimierten und unkomprimierten Versionen einer APEX-Datei.

Unterstützte Komprimierungsalgorithmen

Android 12 unterstützt nur die Deflate-Zip-Komprimierung.

Komprimierte APEX-Datei beim Start aktivieren

Bevor eine komprimierte APEX-Datei aktiviert werden kann, wird die darin enthaltene original_apex-Datei im Verzeichnis /data/apex/decompressed dekomprimiert. Die resultierende dekomprimierte APEX-Datei ist hart mit dem Verzeichnis /data/apex/active verknüpft.

Betrachten Sie das folgende Beispiel zur Verdeutlichung des oben beschriebenen Prozesses.

Betrachten Sie /system/apex/com.android.foo.capex als komprimierte APEX-Datei, die aktiviert wird, mit dem Versionscode 37.

  1. Die Datei original_apex in /system/apex/com.android.foo.capex wird in /data/apex/decompressed/com.android.foo@37.apex dekomprimiert.
  2. Mit restorecon /data/apex/decompressed/com.android.foo@37.apex wird geprüft, ob das SELinux-Label korrekt ist.
  3. Es werden Überprüfungen an /data/apex/decompressed/com.android.foo@37.apex durchgeführt, um seine Gültigkeit zu bestätigen: apexd prüft den in /data/apex/decompressed/com.android.foo@37.apex enthaltenen öffentlichen Schlüssel, um sicherzustellen, dass er mit dem in /system/apex/com.android.foo.capex enthaltenen übereinstimmt.
  4. Die Datei /data/apex/decompressed/com.android.foo@37.apex ist über einen Hardlink mit dem Verzeichnis /data/apex/active/com.android.foo@37.apex verknüpft.
  5. Die reguläre Aktivierungslogik für nicht komprimierte APEX-Dateien wird auf /data/apex/active/com.android.foo@37.apex ausgeführt.

Interaktion mit OTA

Komprimierte APEX-Dateien wirken sich auf die OTA-Bereitstellung und -Anwendung aus. Da ein OTA-Update eine komprimierte APEX-Datei mit einer höheren Versionsebene als die auf dem Gerät aktive Version enthalten kann, muss vor dem Neustart eines Geräts ein bestimmter freier Speicherplatz reserviert werden, um ein OTA-Update anzuwenden.

Zur Unterstützung des OTA-Systems stellt apexd diese beiden Binder-APIs zur Verfügung:

  • calculateSizeForCompressedApex: Berechnet die Größe, die zum Dekomprimieren von APEX-Dateien in einem OTA-Paket erforderlich ist. Damit kann geprüft werden, ob ein Gerät genügend Speicherplatz hat, bevor ein OTA-Update heruntergeladen wird.
  • reserveSpaceForCompressedApex: reserviert Speicherplatz auf dem Laufwerk für die zukünftige Verwendung durch apexd zum Dekomprimieren komprimierter APEX-Dateien im OTA-Paket.

Bei einem A/B-OTA-Update versucht apexd im Rahmen des OTA-Vorgangs nach der Installation, die Dekomprimierung im Hintergrund durchzuführen. Wenn die Dekomprimierung fehlschlägt, führt apexd die Dekomprimierung während des Starts durch, wodurch das OTA-Update angewendet wird.

Bei der Entwicklung von APEX berücksichtigte Alternativen

Im Folgenden finden Sie einige Optionen, die beim Entwerfen des APEX-Dateiformats von AOSP berücksichtigt wurden, und warum sie entweder ein- oder ausgeschlossen wurden.

Reguläre Paketverwaltungssysteme

Linux-Distributionen haben Paketverwaltungssysteme wie dpkg und rpm, die leistungsstark, ausgereift und robust sind. Sie wurden jedoch nicht für APEX angenommen, da sie die Pakete nach der Installation nicht schützen können. Die Überprüfung wird nur durchgeführt, wenn Pakete installiert werden. Angreifer können die Integrität der installierten Pakete unbemerkt schädigen. Dies ist eine Regression für Android, bei der alle Systemkomponenten in schreibgeschützten Dateisystemen gespeichert wurden, deren Integrität für jede E/A durch dm-verity geschützt wird. Jegliche Manipulation von Systemkomponenten muss entweder verboten oder nachweisbar sein, damit das Gerät bei einer Manipulation nicht gestartet werden kann.

dm-crypt für Integrität

Die Dateien in einem APEX-Container stammen aus integrierten Partitionen (z. B. der Partition /system), die durch dm-verity geschützt sind. Dadurch sind Änderungen an den Dateien auch nach der Bereitstellung der Partitionen unzulässig. Um für die Dateien dasselbe Sicherheitsniveau zu bieten, werden alle Dateien in einem APEX in einem Dateisystem-Image gespeichert, das mit einem Hash-Baum und einem vbmeta-Beschreibungselement gekoppelt ist. Ohne dm-verity ist eine APEX-Datei in der /data-Partition anfällig für unbeabsichtigte Änderungen, die nach der Überprüfung und Installation vorgenommen werden.

Tatsächlich ist die Partition /data auch durch Verschlüsselungsebenen wie dm-crypt geschützt. Dies bietet zwar einen gewissen Schutz vor Manipulation, sein Hauptzweck besteht jedoch in der Wahrung der Privatsphäre, nicht der Integrität. Wenn ein Angreifer Zugriff auf die Partition /data erhält, gibt es keinen weiteren Schutz. Auch hier handelt es sich um eine Regression im Vergleich zu jeder Systemkomponente, die sich in der Partition /system befindet. Der Hash-Baum in einer APEX-Datei bietet zusammen mit „dm-verity“ dasselbe Maß an Inhaltsschutz.

Pfade von /system zu /apex weiterleiten

Auf in einer APEX-Datei verpackte Systemkomponentendateien kann über neue Pfade wie /apex/<name>/lib/libfoo.so zugegriffen werden. Als die Dateien Teil der Partition /system waren, waren sie über Pfade wie /system/lib/libfoo.so zugänglich. Ein Client einer APEX-Datei (andere APEX-Dateien oder die Plattform) muss die neuen Pfade verwenden. Aufgrund der Pfadänderung müssen Sie möglicherweise vorhandenen Code aktualisieren.

Eine Möglichkeit, die Pfadänderung zu vermeiden, besteht darin, den Dateiinhalt in einer APEX-Datei auf die /system-Partition zu legen. Das Android-Team hat jedoch beschlossen, die Dateien nicht in der /system-Partition zu überlagern, da dies die Leistung beeinträchtigen könnte, wenn die Anzahl der überlagerten Dateien (möglicherweise sogar eine nach der anderen gestapelte Datei) zunimmt.

Eine weitere Möglichkeit bestand darin, Funktionen für den Dateizugriff wie open, stat und readlink zu manipulieren, sodass Pfade, die mit /system beginnen, zu den entsprechenden Pfaden unter /apex weitergeleitet wurden. Das Android-Team hat diese Option verworfen, da es nicht möglich ist, alle Funktionen zu ändern, die Pfade akzeptieren. Einige Apps verknüpfen Bionic beispielsweise statisch, wodurch die Funktionen implementiert werden. In solchen Fällen werden diese Apps nicht weitergeleitet.