APEX-Dateiformat

Das Containerformat Android Pony EXpress (APEX) wurde in Android 10 eingeführt und wird im Installationsablauf für Systemmodule niedrigerer Ebene verwendet. Dieses Format erleichtert die Aktualisierung von Systemkomponenten, die nicht in das Standard-Android-Anwendungsmodell passen. Einige Beispielkomponenten sind native Dienste und Bibliotheken, Hardware-Abstraktionsschichten ( HALs ), Laufzeitbibliotheken ( ART ) und Klassenbibliotheken.

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

Hintergrund

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

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

Design

In diesem Abschnitt werden das allgemeine Design des APEX-Dateiformats und des APEX-Managers beschrieben, bei dem es sich um einen Dienst handelt, 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 handelt es sich bei einer APEX-Datei um eine ZIP-Datei, in der Dateien unkomprimiert gespeichert werden und sich an einer 4-KB-Grenze 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. Dies ist ein ApexManifest Protokollpuffer im JSON-Format.

Die Datei AndroidManifest.xml ermöglicht der APEX-Datei die Verwendung von APK-bezogenen Tools und Infrastruktur wie ADB, PackageManager und Paketinstallations-Apps (wie Play Store). Beispielsweise kann die APEX-Datei ein vorhandenes Tool wie aapt verwenden, um grundlegende Metadaten aus der Datei zu überprüfen. 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 mit APEX umgehen. 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 mithilfe der libavb Bibliothek erstellt. Die Nutzlast des Dateisystems wird nicht analysiert (da das Image an Ort und Stelle gemountet 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-Images verwendet wird. Zur Laufzeit stellt dieser Schlüssel sicher, dass der heruntergeladene APEX mit derselben Entität signiert wird, die denselben APEX in den integrierten Partitionen signiert.

APEX-Namensrichtlinien

Um Namenskonflikte zwischen neuen APEXes im Zuge der Weiterentwicklung der Plattform zu vermeiden, verwenden Sie die folgenden Benennungsrichtlinien:

  • com.android.*
    • Reserviert für AOSP APEXes. Nicht eindeutig 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 APEXes, 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 früh in der Startsequenz gestartet und ist bereit. APEX-Dateien sind normalerweise auf dem Gerät unter /system/apex vorinstalliert. Der APEX-Manager verwendet diese Pakete standardmäßig, 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. Sobald der Paketmanager erkennt, dass es sich bei der Datei 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 wird, wird die interne Datenbank des APEX-Managers aktualisiert, um anzuzeigen, dass die APEX-Datei beim nächsten Start aktiviert wird.
  5. Der Installationsanforderer 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 aufgelistete 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. Mountet das Device-Mapper-Blockgerät auf einem eindeutigen Pfad (z. B. /apex/ name @ ver ).

Wenn alle in der internen Datenbank aufgeführten APEX-Dateien gemountet sind, stellt der APEX-Manager einen Binder-Dienst für andere Systemkomponenten bereit, um Informationen über die installierten APEX-Dateien abzufragen. Die anderen Systemkomponenten können beispielsweise die Liste der auf dem Gerät installierten APEX-Dateien abfragen oder den genauen Pfad abfragen, in 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 handelt (unter Verwendung des APK-Signaturschemas), die eine AndroidManifest.xml Datei enthalten. Dadurch können APEX-Dateien die Infrastruktur für APK-Dateien nutzen, beispielsweise eine Paketinstallations-App, das Signaturdienstprogramm und den Paketmanager.

Die AndroidManifest.xml Datei in einer APEX-Datei ist minimal und besteht aus dem name , versionCode und optional targetSdkVersion , minSdkVersion und maxSdkVersion für ein fein abgestimmtes Targeting. Diese Informationen ermöglichen die Bereitstellung von APEX-Dateien über bestehende 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.

Signieroptionen

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 der 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 APEXes zu überprüfen, deren Installation angefordert wird. Jeder APEX muss mit unterschiedlichen Schlüsseln signiert werden und wird sowohl zur Build-Zeit als auch zur Laufzeit erzwungen.

APEX in eingebauten Partitionen

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

Wenn ein APEX in einer integrierten Partition vorhanden ist, kann der APEX aktualisiert werden, indem ein APEX-Paket mit demselben Paketnamen und einem Versionscode größer oder gleich bereitgestellt wird. Der neue APEX wird in /data gespeichert und ähnlich wie bei APKs überschattet die neu installierte Version die Version, die bereits in der integrierten Partition vorhanden ist. Aber im Gegensatz zu APKs wird die neu installierte Version von 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-Funktionen erforderlich: der Loopback-Treiber und dm-verity. Der Loopback-Treiber mountet das Dateisystem-Image in einem APEX-Modul 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 Kernel-Versionen 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 Android Common Tree 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 werden und APEX-Module unterstützen möchten. Um die erforderlichen Patches zu erhalten, wird dringend ein Downmerge aus dem android-4.4 Zweig empfohlen. Im Folgenden finden Sie eine Liste der erforderlichen Einzelpatches für die Kernel-Version 4.4.

  • UPSTREAM: Schleife: ioctl hinzufügen, um die logische Blockgröße zu ändern ( 4.4 )
  • BACKPORT: Block/Schleife: set hw_sectors ( 4.4 )
  • UPSTREAM: Schleife: LOOP_SET_BLOCK_SIZE im kompatiblen ioctl ( 4.4 ) hinzufügen
  • ANDROID: mnt: next_descendent reparieren ( 4.4 )
  • ANDROID: mnt: remount sollte sich an Sklaven von Sklaven weitergeben ( 4.4 )
  • ANDROID: mnt: Remount korrekt propagieren ( 4.4 )
  • „ANDROID: dm verity: minimale Prefetch-Größe hinzufügen“ ( 4.4 ) zurücksetzen
  • UPSTREAM: Schleife: Caches löschen, wenn Offset oder Blockgröße geändert werden ( 4.4 )

Kernelversionen 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 einen 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 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

Um APEX zu unterstützen, stellen Sie sicher, dass die Kernel-Befehlszeilenparameter die folgenden Anforderungen erfüllen:

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

Bauen Sie einen APEX

In diesem Abschnitt wird beschrieben, wie Sie einen APEX mit dem Android-Build-System erstellen. Das Folgende ist ein Beispiel für Android.bp für einen 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
Vorgefertigte /etc

Transitive Abhängigkeiten

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

Behandeln Sie mehrere ABIs

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

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

  • Wenn es sich bei dem Gerät nur um ein 32-Bit-Gerät handelt, wird nur die 32-Bit-Variante der Binärdatei installiert.
  • Wenn es sich bei dem Gerät nur um ein 64-Bit-Gerät handelt, wird nur die 64-Bit-Variante der Binärdatei installiert.

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

  • first : Entspricht der primären ABI des Geräts. Dies ist die Standardeinstellung für Binärdateien.
  • lib32 : Entspricht der 32-Bit-ABI des Geräts, sofern unterstützt.
  • lib64 : Entspricht der 64-Bit-ABI des unterstützten Geräts.
  • prefer32 : Entspricht der 32-Bit-ABI des Geräts, sofern unterstützt. Wenn die 32-Bit-ABI nicht unterstützt wird, entspricht sie der 64-Bit-ABI.
  • 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-Signierung

Signieren Sie jeden APEX mit unterschiedlichen Schlüsseln. Wenn ein neuer Schlüssel erforderlich ist, erstellen Sie ein öffentlich-privates Schlüsselpaar und erstellen Sie ein apex_key Modul. Verwenden Sie die key , um den APEX mit dem Schlüssel zu signieren. Der öffentliche Schlüssel wird automatisch im APEX mit dem Namen avb_pubkey eingebunden.

# 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 den APEX geschrieben. Zur Laufzeit überprüft apexd den APEX mithilfe eines öffentlichen Schlüssels mit derselben ID im Gerät.

APEX-Unterzeichnung

Signieren Sie APEXes auf die gleiche Weise wie Sie APKs signieren. APEXes zweimal signieren; einmal für das Mini-Dateisystem (Datei apex_payload.img ) und einmal für die gesamte Datei.

Um einen APEX auf Dateiebene zu signieren, legen Sie die certificate auf eine dieser drei Arten fest:

  • Nicht festgelegt: Wenn kein Wert festgelegt ist, wird der APEX mit dem Zertifikat signiert, das sich unter PRODUCT_DEFAULT_DEV_CERTIFICATE befindet. Wenn kein Flag gesetzt ist, lautet der Pfad standardmäßig build/target/product/security/testkey .
  • <name> : Der APEX wird mit dem Zertifikat <name> im selben Verzeichnis wie PRODUCT_DEFAULT_DEV_CERTIFICATE signiert.
  • :<name> : Der APEX wird mit dem Zertifikat signiert, das vom Soong-Modul namens <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 Sie einen APEX

Um einen APEX zu installieren, verwenden Sie ADB.

adb install apex_file_name
adb reboot

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

adb install --force-non-staged apex_file_name

Verwenden Sie einen APEX

Nach dem Neustart wird der APEX im Verzeichnis /apex/<apex_name>@<version> gemountet. Es können mehrere Versionen desselben APEX gleichzeitig gemountet werden. Unter den Mount-Pfaden ist derjenige, der der neuesten Version entspricht, bind-mounted unter /apex/<apex_name> .

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

APEXes werden typischerweise wie folgt verwendet:

  1. Ein OEM oder ODM lädt bei Auslieferung des Geräts einen 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 ist, zeigt der Pfad nach dem Neustart auf den neuen APEX.

Aktualisieren Sie einen Dienst mit einem APEX

So aktualisieren Sie einen Dienst mithilfe eines APEX:

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

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

Wenn ein als aktualisierbar markierter Dienst startet, bevor die APEXes aktiviert sind, wird der Start verzögert, bis die Aktivierung der APEXes abgeschlossen ist.

Konfigurieren Sie das System 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)

Abgeflachter APEX

Bei älteren Geräten ist es manchmal unmöglich oder undurchführbar, den alten Kernel zu aktualisieren, um APEX vollständig zu unterstützen. Beispielsweise könnte der Kernel ohne CONFIG_BLK_DEV_LOOP=Y erstellt worden sein, was für das Mounten des Dateisystem-Images in einem APEX von entscheidender Bedeutung ist.

Flattened APEX ist ein speziell entwickeltes APEX, das auf Geräten mit einem älteren 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.

Bei der Aktivierung eines abgeflachten APEX ist das Loop-Gerät nicht beteiligt. Das gesamte Verzeichnis /system/apex/my.apex wird direkt an /apex/name@ver gebunden.

Abgeflachte APEXes können nicht durch Herunterladen aktualisierter Versionen der APEXes aus dem Netzwerk aktualisiert werden, da die heruntergeladenen APEXes nicht abgeflacht werden können. Abgeflachte APEXes können nur über einen regulären OTA aktualisiert werden.

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

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

Komprimierte APEXes

Android 12 und höher verfügen über APEX-Komprimierung, um die Speicherauswirkungen aktualisierbarer APEX-Pakete zu reduzieren. Nach der Installation eines APEX-Updates wird die vorinstallierte Version zwar nicht mehr verwendet, belegt aber immer noch denselben Speicherplatz. Der belegte Platz bleibt nicht verfügbar.

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

Die Komprimierung bietet keine Optimierung für Folgendes:

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

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

  • Dynamische gemeinsam genutzte Bibliotheken APEXes. Da apexd immer beide Versionen solcher APEXes aktiviert (vorinstalliert und aktualisiert), bringt ihre Komprimierung 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 handelt es sich bei einer komprimierten APEX-Datei um eine ZIP-Datei, die die ursprüngliche Apex-Datei in deflationierter Form mit einer Komprimierungsstufe von 9 enthält, während andere Dateien unkomprimiert gespeichert werden.

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 Dateien apex_manifest.pb , AndroidManifest.xml und apex_pubkey sind Kopien ihrer entsprechenden Dateien in original_apex .

Erstellen Sie komprimiertes APEX

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

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

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

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

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

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

Vom Build-System generierte komprimierte APEX-Dateien 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 Sie beim Booten eine komprimierte APEX-Datei

Bevor ein komprimierter APEX aktiviert werden kann, wird die darin enthaltene Datei original_apex in das Verzeichnis /data/apex/decompressed dekomprimiert. 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 komprimierten APEX, der aktiviert wird, 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. Für /data/apex/decompressed/com.android.foo@37.apex werden Verifizierungsprüfungen durchgeführt, um die Gültigkeit sicherzustellen: apexd überprüft den in /data/apex/decompressed/com.android.foo@37.apex gebündelten öffentlichen Schlüssel 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 ausgeführt.

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 Versionsstufe 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 auf einem Gerät genügend Speicherplatz vorhanden ist, 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 im Rahmen der Postinstallations-OTA-Routine eine Dekomprimierung im Hintergrund. Wenn die Dekomprimierung fehlschlägt, führt apexd die Dekomprimierung während des Startvorgangs durch, der das OTA-Update anwendet.

Bei der Entwicklung von APEX berücksichtigte Alternativen

Hier sind einige Optionen, die AOSP beim Entwurf des APEX-Dateiformats berücksichtigt hat, und warum sie entweder einbezogen oder ausgeschlossen wurden.

Regelmäßige Paketverwaltungssysteme

Linux-Distributionen verfügen über 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 zerstören. Dies ist eine Regression für Android, bei der alle Systemkomponenten in schreibgeschützten Dateisystemen gespeichert wurden, deren Integrität durch dm-verity für jede E/A geschützt wird. Jegliche Manipulation von Systemkomponenten muss entweder verboten oder erkennbar sein, sodass das Gerät im Falle einer Kompromittierung den Start verweigern 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, wobei jegliche Änderung der Dateien auch nach dem Mounten der Partitionen verboten ist. Um den gleichen Grad an Sicherheit für die Dateien zu bieten, werden alle Dateien in einem APEX in einem Dateisystem-Image gespeichert, das mit einem Hash-Baum und einem vbmeta-Deskriptor gepaart ist. Ohne dm-verity ist ein APEX 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. Obwohl dies ein gewisses Maß an Schutz vor Manipulation bietet, dient es in erster Linie der Privatsphäre und nicht der Integrität. Wenn ein Angreifer Zugriff auf die /data Partition erhält, gibt es keinen weiteren Schutz, und auch dies stellt einen Rückschritt im Vergleich zu allen Systemkomponenten dar, die sich in der /system Partition befinden. Der Hash-Baum in einer APEX-Datei bietet zusammen mit dm-verity das gleiche Maß an Inhaltsschutz.

Leiten Sie Pfade von /system nach /apex um

In einem APEX gepackte Systemkomponentendateien sind über neue Pfade wie /apex/<name>/lib/libfoo.so zugänglich. Wenn die Dateien Teil der /system Partition 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 den vorhandenen Code aktualisieren.

Obwohl eine Möglichkeit, die Pfadänderung zu vermeiden, darin besteht, den Dateiinhalt einer APEX-Datei auf der /system Partition zu überlagern, hat das Android-Team beschlossen, keine Dateien auf der /system Partition zu überlagern, da dies die Leistung beeinträchtigen könnte, da die Anzahl der überlagerten Dateien ( ggf. 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, zu ihren entsprechenden Pfaden unter /apex umgeleitet 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 beispielsweise statisch Bionic, das die Funktionen umsetzt. In solchen Fällen werden diese Apps nicht umgeleitet.