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. Beispiele für Komponenten sind native Dienste und Bibliotheken, Hardwareabstraktionsschichten (HALs), die Laufzeit (ART) und Klassenbibliotheken.

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

Hintergrund

Android unterstützt zwar Updates von Modulen, die dem Standard-App-Modell entsprechen (z. B. Dienste, Aktivitäten), über Paketinstallations-Apps wie die Google Play Store App. Die Verwendung eines ähnlichen Modells für Betriebssystemkomponenten auf niedrigerer Ebene hat jedoch folgende Nachteile:

  • APK-basierte Module können nicht zu Beginn der Bootreihenfolge verwendet werden. Der Paketmanager ist das zentrale Repository für Informationen zu Apps und kann nur über den Aktivitätsmanager gestartet werden, der in einer späteren Phase des Bootvorgangs bereit ist.
  • Das APK-Format (insbesondere das Manifest) ist für Android-Apps konzipiert und Systemmodule eignen sich nicht immer gut dafür.

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

Das 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.

Eine APEX-Datei enthält vier Dateien:

  • 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-Protokoll-Buffer 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 kann beispielsweise ein vorhandenes Tool wie aapt verwendet werden, um grundlegende Metadaten aus der Datei zu prüfen. Die Datei enthält den Paketnamen und Versionsinformationen. 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 Dateisystemnutzlast wird nicht geparst, da das Image an Ort und Stelle bereitgestellt werden sollte. 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. Dieser Schlüssel sorgt dafür, dass die heruntergeladene APEX-Datei zur Laufzeit mit derselben Entität signiert wird, die dieselbe APEX-Datei 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. Nicht eindeutig für ein Unternehmen oder Gerät.
  • com.<companyname>.*
    • Für ein Unternehmen reserviert. Wird möglicherweise von mehreren Geräten dieses Unternehmens verwendet.
  • com.<companyname>.<devicename>.*
    • Reserviert für APEX-Dateien, die nur für ein bestimmtes Gerät (oder eine bestimmte Teilmenge von Geräten) gelten.

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 ist schon früh in der Boot-Sequenz 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.

Bei der Aktualisierungssequenz eines APEX-Objekts 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 überprüft wurde, wird die interne Datenbank des APEX-Managers so aktualisiert, dass die APEX-Datei beim nächsten Start aktiviert wird.
  5. Der Installanfragende erhält eine Nachricht, wenn die Paketüberprüfung erfolgreich war.
  6. Damit die Installation fortgesetzt werden kann, 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 Blockgerät des Gerätemappers ü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. So können die anderen Systemkomponenten beispielsweise die Liste der auf dem Gerät installierten APEX-Dateien oder den genauen Pfad abfragen, unter dem ein bestimmtes APEX bereitgestellt wird, 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) mit einer AndroidManifest.xml-Datei sind. 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. Anhand dieser Informationen können APEX-Dateien über bestehende Kanäle wie Paketinstallations-Apps 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 und der Stabilität der Definitionen der Schnittstellen für die Dateitypen ab.

Signaturoptionen

APEX-Dateien werden auf zwei Arten signiert. Zuerst wird die Datei apex_payload.img (insbesondere der vbmeta-Descriptor, der an apex_payload.img angehängt ist) mit einem Schlüssel signiert. Anschließend wird die gesamte APEX mit dem APK-Signaturschema Version 3 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 verwendet den öffentlichen Schlüssel, um APEX-Anwendungen zu überprüfen, die zur Installation angefordert werden. Jede APEX-Datei muss mit unterschiedlichen Schlüsseln signiert sein. Dies wird sowohl zur Build- als auch zur Laufzeit erzwungen.

APEX in integrierten Partitionen

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

Wenn sich eine APEX-Datei in einer integrierten Partition befindet, kann sie aktualisiert werden, indem Sie ein APEX-Paket mit demselben Paketnamen und einem Versionscode angeben, der mindestens der Version entspricht. 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 der APEX-Datei jedoch erst nach dem Neustart aktiviert.

Kernelanforderungen

Für die Unterstützung von APEX-Mainline-Modulen auf einem Android-Gerät sind die folgenden Linux-Kernelfunktionen erforderlich: der Loopback-Treiber und dm-verity. Der Loopback-Treiber bindet das Dateisystem-Image in einem APEX-Modul an und dm-verity überprüft das APEX-Modul.

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

Unterstützte Kernelversionen

APEX-Mainline-Module werden auf Geräten mit Kernelversion 4.4 oder höher 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 im Android Common Tree enthalten. Verwenden Sie die neueste Version des Android Common Tree, um die Patches für die APEX-Unterstützung zu erhalten.

Kernelversion 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. Um die erforderlichen Patches zu erhalten, wird ein Downmerge aus dem android-4.4-Branch dringend empfohlen. Im Folgenden finden Sie eine Liste der erforderlichen einzelnen Patches für die Kernelversion 4.4.

  • UPSTREAM: loop: ioctl zum Ändern der logischen Blockgröße hinzufügen (4.4)
  • BACKPORT: block/loop: set hw_sectors (4.4)
  • UPSTREAM: loop: LOOP_SET_BLOCK_SIZE in compat ioctl hinzufügen (4.4)
  • ANDROID: mnt: Fehler bei next_descendent beheben (4.4)
  • ANDROID: mnt: remount sollte an untergeordnete Slaves weitergegeben werden (4.4)
  • ANDROID: mnt: Remount korrekt weitergeben (4.4)
  • Rücknahme von „ANDROID: dm verity: add minimum prefetch size“ (4.4)
  • UPSTREAM: loop: Caches löschen, wenn Offset oder Blockgröße geändert werden (4.4)

Kernel-Versionen 4.9/4.14/4.19

Wenn Sie die erforderlichen Patches für die Kernelversionen 4.9, 4.14 und 4.19 benötigen, führen Sie einen Downmerge aus dem android-common-Branch aus.

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

Damit APEX unterstützt wird, müssen die Kernel-Befehlszeilenparameter die folgenden Anforderungen erfüllen:

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

APEX erstellen

In diesem Abschnitt wird beschrieben, wie Sie eine APEX-Datei 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",
}

apex_manifest.json Beispiel:

{
  "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 Standort in APEX
Geteilte Fotogalerien /lib und /lib64 (/lib/arm für übersetzte ARM-Code in x86)
Ausführbare Dateien /bin
Java-Bibliotheken /javalib
Vorkonfigurierte Systeme /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 nur libFoo in der native_shared_libs-Property 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 eine APEX-Anwendung auf Geräte mit einem einzelnen ABI ausgerichtet ist (d. h. nur 32-Bit oder nur 64-Bit), werden nur Bibliotheken mit dem entsprechenden ABI installiert.

Installieren Sie die Property binaries nur für die 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 dem 32-Bit-ABI des Geräts, sofern unterstützt.
  • lib64: Entspricht dem 64-Bit-ABI des unterstützten Geräts.
  • 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: Entspricht beiden ABIs. Das ist die Standardeinstellung für native_shared_libraries.

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

Dieses Beispiel bezieht sich auf ein Gerät, das 32/64 unterstützt und 32 nicht 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 jede APEX-Datei mit einem anderen Schlüssel. Wenn ein neuer Schlüssel erforderlich ist, erstellen Sie ein Schlüsselpaar aus öffentlichem und privatem Schlüssel und erstellen Sie ein apex_key-Modul. Verwenden Sie die Property key, um die APEX-Datei mit dem Schlüssel zu signieren. Der öffentliche Schlüssel wird automatisch mit dem Namen avb_pubkey in APEX aufgenommen.

# 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, ist in der APEX-Datei selbst enthalten. Bei der Laufzeit prüft apexd die APEX mit einem öffentlichen Schlüssel mit derselben ID auf dem Gerät.

APEX-Signatur

Signieren Sie APEX-Dateien auf die gleiche Weise wie APKs. Signieren Sie APEX-Dateien zweimal: einmal für das Mini-Dateisystem (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, lautet der Standardpfad build/target/product/security/testkey.
  • <name>: Die APEX-Datei ist mit dem <name>-Zertifikat im selben Verzeichnis wie PRODUCT_DEFAULT_DEV_CERTIFICATE signiert.
  • :<name>: Die APEX ist mit dem Zertifikat signiert, das vom Soong-Modul namens <name> definiert ist. 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 eine APEX-Datei zu installieren.

adb install apex_file_name
adb reboot

Wenn supportsRebootlessUpdate in apex_manifest.json auf true gesetzt ist und die aktuell installierte APEX nicht verwendet wird (z. B. alle darin enthaltenen Dienste beendet wurden), kann eine neue 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. Derjenige der Bereitstellungspfade, der der neuesten Version entspricht, wird unter /apex/<apex_name> bereitgestellt.

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

APEX-Objekte 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. Der Zugriff auf Dateien im APEX erfolgt über den Pfad /apex/<apex_name>/.
  3. Wenn eine aktualisierte Version des APEX in /data/apex installiert wird, verweist der Pfad nach dem Neustart auf das neue APEX.

Dienst mit einer APEX-Anwendung aktualisieren

So aktualisieren Sie einen Dienst mithilfe einer APEX-Domain:

  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 APEX-Klassen 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. Möglicherweise wurde der Kernel beispielsweise ohne CONFIG_BLK_DEV_LOOP=Y erstellt, was für das Bereitstellen des Dateisystem-Images in einem APEX-Image erforderlich ist.

Flattened APEX ist eine speziell entwickelte APEX-Datei, die auf Geräten mit einem alten 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 einer flachen APEX-Datei wird das Loop-Gerät nicht verwendet. Das gesamte Verzeichnis /system/apex/my.apex wird direkt mit /apex/name@ver verbunden.

Abgeflachte APEX-Objekte können nicht durch Herunterladen aktualisierter Versionen der APEX-Objekte aus dem Netzwerk aktualisiert werden, da die heruntergeladenen APEX-Objekte nicht abgeflacht werden können. Zusammengeführte APEX-Dokumente können nur über eine reguläre OTA-Datei aktualisiert werden.

Die flache APEX-Konfiguration 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).

Die Kombination von abgeflachenen und nicht abgeflachenen APEX-Objekten auf einem Gerät wird NICHT unterstützt. APEX-Objekte in einem Gerät müssen entweder alle nicht abgeflattet oder alle abgeflattet sein. Das ist besonders wichtig, wenn Sie vorsignierte APEX-Prebuilts für Projekte wie Mainline bereitstellen. APEX-Objekte, die nicht vorab signiert wurden (d. h. aus der Quelle erstellt wurden), sollten auch nicht flachgelegt und mit den richtigen Schlüsseln signiert werden. Das Gerät sollte von updatable_apex.mk erben, wie unter Dienst mit einem APEX aktualisieren erläutert.

Komprimierte APEX-Dateien

Android 12 und höher bieten die APEX-Komprimierung, um die Speicherbelastung durch aktualisierbare APEX-Pakete zu reduzieren. Nachdem ein Update für eine APEX installiert wurde, belegt die vorinstallierte Version zwar nicht mehr den Speicherplatz, nimmt aber weiterhin denselben Speicherplatz in Anspruch. Dieser belegte Speicherplatz ist weiterhin nicht verfügbar.

Die APEX-Komprimierung minimiert diese Speicherauswirkungen, indem eine stark komprimierte Gruppe von APEX-Dateien auf nur lesbaren Partitionen (z. B. der /system-Partition) verwendet wird. Android 12 und höher verwenden den DEFLATE-Zip-Komprimierungsalgorithmus.

Die Komprimierung bietet keine Optimierung für Folgendes:

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

  • Nicht aktualisierbare APEX-Dateien 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 APEX-Objekte finden Sie auf der Seite Modulare Systemkomponenten.

  • APEX-Objekte mit dynamischen freigegebenen Bibliotheken Da apexd immer beide Versionen solcher APEX-Dateien (vorinstalliert und aktualisiert) aktiviert, bringt eine Komprimierung keinen Mehrwert.

Komprimiertes APEX-Dateiformat

Das ist das Format einer komprimierten APEX-Datei.

Diagramm zum 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 in deflatierter Form mit einer Komprimierungsstufe von 9 und andere nicht komprimierte Dateien enthält.

Eine APEX-Datei besteht aus vier Dateien:

  • original_apex: entpackt mit Komprimierungsstufe 9. Dies ist die ursprüngliche, nicht komprimierte APEX-Datei.
  • 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.

Komprimierte APEX-Datei erstellen

Komprimierte APEX-Dateien können mit dem apex_compression_tool.py-Tool unter system/apex/tools erstellt werden.

Im Build-System sind mehrere Parameter für die 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,
}

Mit einem PRODUCT_COMPRESSED_APEX-Produktflag wird festgelegt, 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 werden, haben die Endung .capex. Die Erweiterung erleichtert die Unterscheidung zwischen komprimierten und nicht komprimierten Versionen einer APEX-Datei.

Unterstützte Komprimierungsalgorithmen

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

Komprimierte APEX-Datei beim Starten 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 mit dem Verzeichnis /data/apex/active hart verknüpft.

Das folgende Beispiel veranschaulicht den oben beschriebenen Prozess.

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. restorecon /data/apex/decompressed/com.android.foo@37.apex wird ausgeführt, um zu prüfen, ob das richtige SELinux-Label vorhanden 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 haben Auswirkungen auf die OTA-Bereitstellung und -Anwendung. Da ein Over-the-air-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 Over-the-air-Update anzuwenden.

Zur Unterstützung des OTA-Systems stellt apexd die folgenden beiden Binder APIs bereit:

  • calculateSizeForCompressedApex: Berechnet die Größe, die zum Dekomprimieren von APEX-Dateien in einem OTA-Paket erforderlich ist. So lässt sich prüfen, ob auf einem Gerät genügend Speicherplatz vorhanden ist, bevor ein Over-the-air-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 Bootens aus, um das OTA-Update anzuwenden.

Bei der Entwicklung von APEX in Betracht gezogene Alternativen

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

Regelmäßige Paketverwaltungssysteme

Linux-Distributionen haben Paketverwaltungssysteme wie dpkg und rpm, die leistungsstark, ausgereift und robust sind. Sie wurden jedoch nicht für APEX übernommen, da sie die Pakete nach der Installation nicht schützen können. Die Überprüfung erfolgt nur, 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 bei jeder E/A durch dm-verity geschützt ist. 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 /system-Partition), die durch dm-verity geschützt sind. Dort sind Änderungen an den Dateien auch nach dem Bereitstellen der Partitionen nicht zulä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 /data-Partition auch durch Verschlüsselungsschichten 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 /data-Partition erhält, kann es keinen weiteren Schutz geben. Dies ist im Vergleich dazu, dass sich alle Systemkomponenten in der /system-Partition befinden, ein Rückschritt. Der Hash-Baum in einer APEX-Datei bietet zusammen mit dm-verity denselben Inhaltsschutz.

Pfade von /system zu /apex weiterleiten

Auf Dateien von Systemkomponenten, die in einer APEX-Datei verpackt sind, kann über neue Pfade wie /apex/<name>/lib/libfoo.so zugegriffen werden. Wenn die Dateien Teil der Partition /system waren, war über Pfade wie /system/lib/libfoo.so auf sie zuzugreifen. Ein Client einer APEX-Datei (andere APEX-Dateien oder die Plattform) muss die neuen Pfade verwenden. Möglicherweise müssen Sie aufgrund der Pfadänderung vorhandenen Code aktualisieren.

Eine Möglichkeit, die Pfadänderung zu vermeiden, besteht darin, den Dateiinhalt in einer APEX-Datei auf die /system-Partition zu überlagern. Das Android-Team hat sich jedoch entschieden, keine Dateien auf die /system-Partition zu überlagern, da dies die Leistung beeinträchtigen könnte, wenn die Anzahl der überlagerten Dateien (möglicherweise sogar übereinander gestapelt) 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 diesen Fällen werden diese Apps nicht weitergeleitet.