APEX-Dateiformat

Das Containerformat Android Pony EXpress (APEX) wurde in Android 10 eingeführt und wird im Installationsablauf für untergeordnete Systemmodule verwendet. Dieses Format erleichtert die Aktualisierung von Systemkomponenten, die nicht in das standardmäßige Android-Anwendungsmodell passen. Einige Beispielkomponenten sind native Dienste und Bibliotheken, Hardwareabstraktionsschichten ( HALs ), Laufzeit ( ART ) und Klassenbibliotheken.

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

Hintergrund

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

  • APK-basierte Module können nicht früh in der Startsequenz verwendet werden. Der Paketmanager ist die zentrale Ablage für Informationen über Apps und kann nur aus dem Aktivitätsmanager gestartet werden, der in einem späteren Stadium des Bootvorgangs bereitsteht.
  • Das APK-Format (insbesondere das Manifest) ist für Android-Apps konzipiert, und Systemmodule passen nicht immer gut.

Entwurf

Dieser Abschnitt beschreibt das High-Level-Design des APEX-Dateiformats und des APEX-Managers, eines Dienstes, der APEX-Dateien verwaltet.

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 gespeichert werden und sich an 4-KB-Grenzen befinden.

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.

Die Datei AndroidManifest.xml “ ermöglicht der APEX-Datei die Verwendung von APK-bezogenen Tools und Infrastrukturen wie ADB, PackageManager und Paketinstallationsprogramm-Apps (z. B. Play Store). Beispielsweise kann die APEX-Datei ein vorhandenes Tool wie aapt , um grundlegende Metadaten aus der Datei zu untersuchen. Die Datei enthält Paketnamen und Versionsinformationen. Diese Informationen sind im Allgemeinen auch in apex_manifest.json verfügbar.

apex_manifest.json wird gegenüber AndroidManifest.xml für neuen Code und Systeme empfohlen, die sich mit APEX befassen. AndroidManifest.xml enthält möglicherweise zusätzliche Targeting-Informationen, die von den vorhandenen App-Veröffentlichungstools 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 gemountet. Insbesondere werden der Hash-Baum und der Metadatenblock mit der libavb Bibliothek erstellt. Die Nutzlast des Dateisystems wird nicht analysiert (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, der zum Signieren des Dateisystem-Image verwendet wird. Zur Laufzeit stellt dieser Schlüssel sicher, dass das heruntergeladene APEX mit derselben Entität signiert wird, die dasselbe APEX in den integrierten Partitionen signiert.

APEX-Namensrichtlinien

Verwenden Sie die folgenden Benennungsrichtlinien, um Namenskonflikte zwischen neuen APEXs zu vermeiden, wenn die Plattform voranschreitet:

  • com.android.*
    • Reserviert für AOSP-APEX. Nicht einzigartig für ein Unternehmen oder Gerät.
  • com.<companyname>.*
    • Reserviert für ein Unternehmen. Wird möglicherweise von mehreren Geräten dieses Unternehmens verwendet.
  • com.<companyname>.<devicename>.*
    • Reserviert für APEXs, die nur für ein bestimmtes Gerät (oder eine Teilmenge von Geräten) gelten.

APEX-Manager

Der APEX-Manager (oder 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 früh in der Startsequenz bereit. APEX-Dateien sind normalerweise auf dem Gerät unter /system/apex vorinstalliert. Der APEX-Manager verwendet standardmäßig diese Pakete, wenn keine Updates verfügbar sind.

Die Aktualisierungssequenz eines APEX verwendet die PackageManager-Klasse und ist wie folgt.

  1. Eine APEX-Datei wird über eine Paketinstallations-App, ADB oder eine andere Quelle heruntergeladen.
  2. Der Paketmanager startet den Installationsvorgang. Beim Erkennen, dass die Datei ein APEX ist, überträgt der Paketmanager die Kontrolle an den APEX-Manager.
  3. Der APEX-Manager überprüft die APEX-Datei.
  4. Wenn die APEX-Datei überprüft wird, wird die interne Datenbank des APEX-Managers aktualisiert, um widerzuspiegeln, dass die APEX-Datei beim nächsten Start aktiviert wird.
  5. Der Installations-Anforderer erhält nach erfolgreicher Paketüberprüfung eine Rundsendung.
  6. Um die Installation fortzusetzen, muss das System neu gestartet werden.
  7. Beim nächsten Start startet der APEX-Manager, liest die interne Datenbank und führt für jede aufgeführte APEX-Datei Folgendes aus:

    1. Überprüft die APEX-Datei.
    2. Erstellt ein Loopback-Gerät aus der APEX-Datei.
    3. Erstellt ein Device-Mapper-Blockgerät auf dem Loopback-Gerät.
    4. Stellt das Device-Mapper-Blockgerät auf einem eindeutigen Pfad bereit (z. B. /apex/ name @ ver ).

Wenn alle in der internen Datenbank aufgelisteten APEX-Dateien gemountet sind, stellt der APEX-Manager einen Bindedienst für andere Systemkomponenten bereit, um Informationen über die installierten APEX-Dateien abzufragen. Beispielsweise können die anderen Systemkomponenten die Liste der auf dem Gerät installierten APEX-Dateien abfragen oder den genauen Pfad abfragen, an dem ein bestimmtes APEX gemountet ist, damit auf die Dateien zugegriffen werden kann.

APEX-Dateien sind APK-Dateien

APEX-Dateien sind gültige APK-Dateien, da es sich um signierte ZIP-Archive (unter Verwendung des APK-Signaturschemas) handelt, die eine AndroidManifest.xml -Datei enthalten. Dadurch können APEX-Dateien die Infrastruktur für APK-Dateien verwenden, z. B. eine Paketinstallations-App, das Signaturdienstprogramm und den Paketmanager.

Die Datei AndroidManifest.xml in einer APEX-Datei ist minimal und besteht aus dem name , versionCode und optional targetSdkVersion , minSdkVersion und maxSdkVersion für eine feinkörnige Ausrichtung. Diese Informationen ermöglichen die Bereitstellung von APEX-Dateien über vorhandene Kanäle wie Paketinstallations-Apps und ADB.

Unterstützte Dateitypen

Das APEX-Format unterstützt diese Dateitypen:

  • Native gemeinsam genutzte Bibliotheken
  • Native ausführbare Dateien
  • JAR-Dateien
  • Datei
  • Konfigurationsdateien

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

Unterzeichnung

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

Auf der Geräteseite wird ein öffentlicher Schlüssel installiert, der dem privaten Schlüssel entspricht, der zum Signieren des vbmeta-Deskriptors verwendet wird. Der APEX-Manager verwendet den öffentlichen Schlüssel, um APEXs zu überprüfen, deren Installation angefordert wird. Jeder APEX muss mit unterschiedlichen Schlüsseln signiert werden und wird sowohl zur Erstellungszeit als auch zur Laufzeit erzwungen.

APEX in integrierten Partitionen

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

Wenn ein APEX in einer integrierten Partition vorhanden ist, kann das APEX aktualisiert werden, indem ein APEX-Paket mit demselben Paketnamen und einem Versionscode größer oder gleich bereitgestellt wird. Das neue APEX wird in /data gespeichert und ähnlich wie bei APKs spiegelt die neu installierte Version die Version, die bereits in der integrierten Partition vorhanden ist. Aber im Gegensatz zu APKs wird die neu installierte Version des APEX erst nach einem Neustart aktiviert.

Kernel-Anforderungen

Um APEX Mainline-Module auf einem Android-Gerät zu unterstützen, sind die folgenden Linux-Kernel-Features 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 von 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-Version 4.4 oder höher unterstützt. Neue Geräte, die mit Android 10 oder höher gestartet werden, müssen die Kernel-Version 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 allgemeinen Android-Baum enthalten. Um die Patches zur Unterstützung von APEX zu erhalten, verwenden Sie die neueste Version des Android Common Tree.

Kernel-Version 4.4

Diese Version wird nur für Geräte unterstützt, die von Android 9 auf Android 10 aktualisiert wurden und APEX-Module unterstützen möchten. Um die erforderlichen Patches zu erhalten, wird ein Down-Merge vom android-4.4 Zweig dringend empfohlen. Im Folgenden finden Sie eine Liste der erforderlichen individuellen Patches für die Kernel-Version 4.4.

  • UPSTREAM: Schleife: ioctl zum Ändern der logischen Blockgröße hinzufügen ( 4.4 )
  • BACKPORT: Block/Schleife: setze hw_sectors ( 4.4 )
  • UPSTREAM: Schleife: LOOP_SET_BLOCK_SIZE in Compat ioctl hinzufügen ( 4.4 )
  • ANDROID: mnt: Fix next_descendent ( 4.4 )
  • ANDROID: mnt: Remount sollte an Slaves von Slaves weitergegeben werden ( 4.4 )
  • ANDROID: mnt: Remount korrekt propagieren ( 4.4 )
  • Zurücksetzen von „ANDROID: dm verity: minimale Prefetch-Größe hinzufügen“ ( 4.4 )
  • UPSTREAM: Schleife: Caches löschen, wenn Offset oder Blockgröße geändert werden ( 4.4 )

Kernel-Versionen 4.9/4.14/4.19

Um die erforderlichen Patches für die Kernel-Versionen 4.9/4.14/4.19 zu erhalten, führen Sie eine Downmerge vom android-common Zweig durch.

Erforderliche Kernel-Konfigurationsoptionen

Die folgende Liste zeigt die grundlegenden Konfigurationsanforderungen für die Unterstützung von APEX-Modulen, die in Android 10 eingeführt wurden. Die Elemente mit einem Sternchen (*) sind vorhandene 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

Stellen Sie zur Unterstützung von APEX sicher, dass die Kernel-Befehlszeilenparameter die folgenden Anforderungen erfüllen:

  • loop.max_loop darf NICHT gesetzt sein
  • loop.max_part muss <= 8 sein

Aufbau eines APEX

In diesem Abschnitt wird beschrieben, wie Sie ein APEX mit dem Android-Buildsystem erstellen. Das Folgende ist ein Beispiel für Android.bp für ein 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
Gemeinsam genutzte Bibliotheken /lib und /lib64 ( /lib/arm für übersetzten arm in x86)
Ausführbare Dateien /bin
Java-Bibliotheken /javalib
Vorgefertigt /etc

Transitive Abhängigkeiten

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

Umgang mit mehreren ABIs

Installieren Sie die Eigenschaft native_shared_libs für die primären und sekundären binären Anwendungsschnittstellen (ABIs) des Geräts. Wenn ein APEX auf Geräte mit einer einzelnen ABI abzielt (d. h. nur 32 Bit oder nur 64 Bit), werden nur Bibliotheken mit der entsprechenden ABI installiert.

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

  • Wenn das Gerät nur 32-Bit ist, wird nur die 32-Bit-Variante der Binärdatei installiert.
  • Wenn das Gerät nur 64-Bit ist, wird nur die 64-Bit-Variante der Binärdatei installiert.

Um eine feinkörnige Kontrolle über die ABIs der nativen Bibliotheken und Binärdateien hinzuzufügen, verwenden Sie die multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries] .

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

Die Eigenschaften java , libraries und prebuilts sind ABI-agnostisch.

Dieses Beispiel gilt für 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 signieren

Signieren Sie jeden APEX mit unterschiedlichen Schlüsseln. Wenn ein neuer Schlüssel erforderlich ist, erstellen Sie ein öffentlich-privates Schlüsselpaar und ein apex_key -Modul. Verwenden Sie die key , um das APEX mit dem Schlüssel zu signieren. Der öffentliche Schlüssel wird automatisch mit dem Namen avb_pubkey in das 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, der zum Signieren eines APEX verwendet wird, wird in das APEX geschrieben. Zur Laufzeit verifiziert apexd den APEX mithilfe eines öffentlichen Schlüssels mit derselben ID im Gerät.

ZIP-Signatur

Signieren Sie APEXs genauso wie APKs. Signieren Sie APEXs zweimal; einmal für das Mini-Dateisystem (Datei apex_payload.img ) und einmal für die gesamte Datei.

Um ein APEX auf Dateiebene zu signieren, legen Sie die certificate auf eine der folgenden drei Arten fest:

  • Nicht festgelegt: Wenn kein Wert festgelegt ist, wird das APEX mit dem Zertifikat signiert, das sich unter PRODUCT_DEFAULT_DEV_CERTIFICATE . Wenn kein Flag gesetzt ist, lautet der Pfad standardmäßig build/target/product/security/testkey .
  • <name> : Das APEX ist mit dem <name> -Zertifikat im selben Verzeichnis wie PRODUCT_DEFAULT_DEV_CERTIFICATE .
  • :<name> : Das APEX wird mit dem Zertifikat signiert, das durch das Soong-Modul mit dem Namen <name> definiert wird. Das Zertifikatsmodul kann wie folgt 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)
}

Installieren eines APEX

Um ein APEX zu installieren, verwenden Sie ADB.

adb install apex_file_name
adb reboot

Verwenden eines APEX

Nach dem Neustart wird das APEX im /apex/<apex_name>@<version> . Mehrere Versionen desselben APEX können gleichzeitig montiert werden. Unter den Bereitstellungspfaden wird derjenige, der der neuesten Version entspricht, unter /apex/<apex_name> Bind-Mount bereitgestellt.

Clients können den Bind-Mounted-Pfad verwenden, um Dateien von APEX zu lesen oder auszuführen.

APEXs werden typischerweise wie folgt verwendet:

  1. Ein OEM oder ODM lädt ein APEX vorab unter /system/apex , wenn das Gerät ausgeliefert wird.
  2. Auf Dateien im APEX wird über den Pfad /apex/<apex_name>/ .
  3. Wenn eine aktualisierte Version des APEX in /data/apex installiert ist, zeigt der Pfad nach dem Neustart auf das neue APEX.

Aktualisieren eines Dienstes mit einem APEX

So aktualisieren Sie einen Dienst mit einem APEX:

  1. Markieren Sie den Dienst in der Systempartition als aktualisierbar. Fügen Sie die Option updatable zur Dienstdefinition 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 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
    

Service-Definitionen können nur in der .rc -Datei eines APEX definiert werden. Aktionsauslöser werden in APEXes nicht unterstützt.

Wenn ein als aktualisierbar gekennzeichneter Dienst gestartet wird, bevor die APEXs aktiviert sind, wird der Start verzögert, bis die Aktivierung der APEXs abgeschlossen ist.

Konfigurieren des Systems zur Unterstützung von APEX-Updates

Setzen Sie die folgende Systemeigenschaft auf „ true “, um APEX-Dateiaktualisierungen zu unterstützen.

<device.mk>:

PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true

BoardConfig.mk:
TARGET_FLATTEN_APEX := false

oder nur

<device.mk>:

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

Abgeflachte Spitze

Bei älteren Geräten ist es manchmal unmöglich oder unmöglich, den alten Kernel zu aktualisieren, um APEX vollständig zu unterstützen. Beispielsweise könnte der Kernel ohne CONFIG_BLK_DEV_LOOP=Y worden sein, was für das Mounten 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 einem abgeflachten APEX werden direkt in einem Verzeichnis unter der integrierten Partition installiert. Beispielsweise wird lib/libFoo.so in einem abgeflachten APEX my.apex in /system/apex/my.apex/lib/libFoo.so installiert.

Das Aktivieren eines abgeflachten APEX betrifft nicht das Loop-Gerät. Das gesamte Verzeichnis /system/apex/my.apex ist direkt an /apex/name@ver gebunden.

Abgeflachte APEX-Dateien können nicht aktualisiert werden, indem aktualisierte Versionen der APEX-Dateien aus dem Netzwerk heruntergeladen werden, da die heruntergeladenen APEX-Dateien nicht abgeflacht werden können. Abgeflachte APEXs können nur über ein reguläres OTA aktualisiert werden.

Abgeflachtes APEX ist die Standardkonfiguration. Das bedeutet, dass alle APEXs standardmäßig abgeflacht sind, es sei denn, Sie konfigurieren Ihr Gerät explizit so, dass nicht abgeflachte APEXs erstellt werden, um APEX-Updates zu unterstützen (wie oben erläutert).

Das Mischen von abgeflachten und nicht abgeflachten APEXs in einem Gerät wird NICHT unterstützt. APEXs in einem Gerät müssen entweder alle nicht abgeflacht oder alle abgeflacht sein. Dies ist besonders wichtig, wenn Sie vorsignierte APEX-Prebuilds für Projekte wie Mainline versenden. APEXs, die nicht vorsigniert sind (d. h. aus der Quelle erstellt wurden), sollten ebenfalls nicht abgeflacht und mit den richtigen Schlüsseln signiert werden. Das Gerät sollte von updatable_apex.mk erben, wie in Aktualisieren eines Dienstes mit einem APEX erläutert.

Komprimierte APEXs

Android 12 und höher verfügen über die APEX-Komprimierung, um die Speicherbelastung durch aktualisierbare APEX-Pakete zu reduzieren. Nachdem ein Update auf ein APEX installiert wurde, belegt es, obwohl seine vorinstallierte Version nicht mehr verwendet wird, immer noch die gleiche Menge an Speicherplatz. Dieser besetzte Platz bleibt nicht verfügbar.

Die APEX-Komprimierung minimiert diese Auswirkungen auf den Speicher, indem ein stark komprimierter Satz von APEX-Dateien auf schreibgeschützten Partitionen (z. B. der /system Partition) verwendet wird. Android 12 und höher verwenden einen DEFLATE-Zip-Komprimierungsalgorithmus.

Die Komprimierung bietet keine Optimierung für Folgendes:

  • Bootstrap-APEXs, die sehr früh in der Startsequenz gemountet werden müssen.

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

  • Dynamische gemeinsam genutzte Bibliotheken APEXes. Da apexd immer beide Versionen solcher APEXs (vorinstalliert und aktualisiert) aktiviert, bringt das Komprimieren keinen Mehrwert.

Komprimiertes APEX-Dateiformat

Dies ist das Format einer komprimierten APEX-Datei.

Diagram shows the format of a compressed APEX file

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 mit anderen unkomprimiert gespeicherten Dateien enthält.

Vier Dateien umfassen eine APEX-Datei:

  • original_apex : deflationiert mit Komprimierungsstufe 9 Dies ist die ursprüngliche, unkomprimierte APEX-Datei .
  • apex_manifest.pb : nur gespeichert
  • AndroidManifest.xml : nur gespeichert
  • apex_pubkey : nur gespeichert

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

Komprimiertes APEX erstellen

Komprimiertes APEX kann mit dem Tool apex_compression_tool.py erstellt werden, das sich unter system/apex/tools befindet.

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

Ob eine APEX-Datei komprimierbar ist, wird in Android.bp durch die Eigenschaft compressible gesteuert:

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

Ein PRODUCT_COMPRESSED_APEX Produktflag steuert, ob ein aus der Quelle erstelltes Systemabbild komprimierte APEX-Dateien enthalten muss.

Für lokale Experimente können Sie einen Build zwingen, APEXs zu komprimieren, indem OVERRIDE_PRODUCT_COMPRESSED_APEX= auf true setzen.

Komprimierte APEX-Dateien, die vom Build-System generiert werden, 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.

Aktivieren einer komprimierten APEX-Datei während des Bootens

Bevor ein komprimiertes APEX aktiviert werden kann, wird die darin enthaltene Datei original_apex in das Verzeichnis /data/apex/decompressed . Die resultierende dekomprimierte APEX-Datei ist fest mit dem Verzeichnis /data/apex/active verknüpft.

Betrachten Sie das folgende Beispiel als Veranschaulichung des oben beschriebenen Prozesses.

Betrachten Sie /system/apex/com.android.foo.capex als aktiviertes komprimiertes APEX mit 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 überprüfen, ob es ein korrektes SELinux-Label hat.
  3. Verifizierungsprüfungen werden auf /data/apex/decompressed/com.android.foo@37.apex durchgeführt, um seine Gültigkeit sicherzustellen: apexd überprüft den öffentlichen Schlüssel, der in /data/apex/decompressed/com.android.foo@37.apex gebündelt ist Stellen Sie sicher, dass es mit dem in /system/apex/com.android.foo.capex gebündelten identisch ist.
  4. Die Datei /data/apex/decompressed/com.android.foo@37.apex ist fest mit dem Verzeichnis /data/apex/active/com.android.foo@37.apex verknüpft.
  5. Die reguläre Aktivierungslogik für unkomprimierte APEX-Dateien wird auf /data/apex/active/com.android.foo@37.apex .

Interaktion mit OTA

Komprimierte APEX-Dateien haben Auswirkungen auf die OTA-Bereitstellung und -Anwendung. Da ein OTA-Update möglicherweise eine komprimierte APEX-Datei mit einer höheren Versionsebene als der auf einem Gerät aktiven enthält, muss eine bestimmte Menge an freiem Speicherplatz reserviert werden, bevor ein Gerät neu gestartet wird, 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. Dies kann verwendet werden, um zu überprüfen, ob ein Gerät über genügend Speicherplatz verfügt, bevor ein OTA heruntergeladen wird.
  • reserveSpaceForCompressedApex – reserviert Speicherplatz auf der Festplatte für die zukünftige Verwendung durch apexd zum Dekomprimieren komprimierter APEX-Dateien innerhalb des OTA-Pakets.

Im Falle eines A/B-OTA-Updates versucht apexd die Dekomprimierung im Hintergrund als Teil der OTA-Routine nach der Installation. Wenn die Dekomprimierung fehlschlägt, führt apexd die Dekomprimierung während des Bootvorgangs durch, der das OTA-Update anwendet.

Bei der Entwicklung von APEX berücksichtigte Alternativen

Hier sind einige Optionen, die AOSP beim Entwerfen des APEX-Dateiformats berücksichtigt hat, und warum sie entweder eingeschlossen 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 übernommen, 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 unbemerkt die Integrität der installierten Pakete knacken. Dies ist eine Regression für Android, bei der alle Systemkomponenten in schreibgeschützten Dateisystemen gespeichert wurden, deren Integrität von dm-verity für jede E/A geschützt wird. Jede Manipulation an Systemkomponenten muss entweder verboten oder erkennbar sein, damit das Gerät den Start verweigern kann, wenn es kompromittiert wird.

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, wobei jede Änderung an den Dateien auch nach dem Mounten der Partitionen verboten ist. Um den Dateien dieselbe Sicherheitsebene zu bieten, werden alle Dateien in einem APEX in einem Dateisystem-Image gespeichert, das mit einem Hash-Baum und einem vbmeta-Deskriptor gekoppelt ist. Ohne dm-verity ist ein APEX in der /data Partition anfällig für unbeabsichtigte Änderungen, die vorgenommen werden, nachdem es überprüft und installiert wurde.

Tatsächlich ist die /data Partition auch durch Verschlüsselungsschichten wie dm-crypt geschützt. Obwohl dies ein gewisses Maß an Schutz vor Manipulationen bietet, ist sein Hauptzweck der Datenschutz, nicht die Integrität. Wenn ein Angreifer Zugriff auf die /data Partition erhält, kann es keinen weiteren Schutz geben, und dies ist wiederum ein Rückschritt im Vergleich zu jeder Systemkomponente, die sich in der /system Partition befindet. Der Hash-Baum in einer APEX-Datei bietet zusammen mit dm-verity das gleiche Maß an Inhaltsschutz.

Pfade von /system nach /apex umleiten

In einem APEX gepackte Systemkomponentendateien sind über neue Pfade wie /apex/<name>/lib/libfoo.so zugänglich. 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.

Obwohl eine Möglichkeit, die Pfadänderung zu vermeiden, darin besteht, den Dateiinhalt in einer APEX-Datei auf der /system Partition zu überlagern, entschied sich das Android-Team, keine Dateien auf der /system Partition zu überlagern, da dies die Leistung beeinträchtigen könnte, da die Anzahl der überlagerten Dateien ( evtl. sogar hintereinander gestapelt) erhöht.

Eine andere Möglichkeit bestand darin, Dateizugriffsfunktionen wie open , stat und readlink zu kapern, sodass Pfade, die mit /system beginnen, auf ihre entsprechenden Pfade unter /apex umgeleitet wurden. Das Android-Team hat diese Option verworfen, da es unmöglich ist, alle Funktionen zu ändern, die Pfade akzeptieren. Beispielsweise binden einige Apps Bionic statisch ein, das die Funktionen implementiert. In solchen Fällen werden diese Apps nicht umgeleitet.