Das APEX-Containerformat (Android Pony EXpress) wurde in Android 10 eingeführt und wird im Installationsablauf für Systemmodule auf niedrigerer Ebene verwendet. Dieses Format erleichtert die Aktualisierung von Systemkomponenten, die nicht in das Standardmodell für Android-Anwendungen passen. Beispiele für Komponenten sind native Dienste und Bibliotheken, Hardware-Abstraktionsebenen (HALs), die Laufzeitumgebung (ART) und Klassenbibliotheken.
Der Begriff „APEX“ kann sich auch auf eine APEX-Datei beziehen.
Hintergrund
Android unterstützt zwar Updates von Modulen, die in das Standard-App-Modell passen (z. B. Dienste, Aktivitäten) über Paketinstallations-Apps (z. B. die Google Play Store App), die Verwendung eines ähnlichen Modells für Betriebssystemkomponenten auf niedrigerer Ebene hat jedoch die folgenden Nachteile:
- APK-basierte Module können nicht früh im Bootvorgang verwendet werden. Der Paketmanager ist das zentrale Informations-Repository für 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 eignet sich nicht immer für Systemmodule.
Design
In diesem Abschnitt wird das allgemeine Design des APEX-Dateiformats und des APEX-Managers beschrieben, einem Dienst, der APEX-Dateien verwaltet.
Weitere Informationen dazu, warum dieses Design für APEX ausgewählt wurde, finden Sie unter Alternativen, die bei der Entwicklung von APEX in Betracht gezogen wurden.
APEX-Format
Dies ist das Format einer APEX-Datei.
Abbildung 1: APEX-Dateiformat
Auf der obersten Ebene ist eine APEX-Datei eine ZIP-Datei, in der Dateien unkomprimiert und an 4‑KB-Grenzen gespeichert werden.
Die vier Dateien in einer APEX-Datei sind:
apex_manifest.json
AndroidManifest.xml
apex_payload.img
apex_pubkey
Die Datei apex_manifest.json
enthält den Paketnamen und die Version, die eine APEX-Datei identifizieren. Dies ist ein
ApexManifest
-Protokollpuffer im JSON-Format.
Mit der Datei AndroidManifest.xml
kann die APEX-Datei APK-bezogene Tools und Infrastruktur wie ADB, PackageManager und Paketinstallations-Apps (z. B. Play Store) 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 die Versionsinformationen. Diese Informationen sind in der Regel auch in apex_manifest.json
verfügbar.
apex_manifest.json
wird für neuen Code und Systeme, die APEX verwenden, gegenüber AndroidManifest.xml
empfohlen. AndroidManifest.xml
kann zusätzliche Targeting-Informationen enthalten, 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 eingebunden. Hash-Baum und Metadatenblock werden mit der libavb
-Bibliothek erstellt. Die Nutzlast des Dateisystems wird nicht geparst, da das Image direkt gemountet werden soll. 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 sorgt dieser Schlüssel dafür, dass das heruntergeladene APEX mit derselben Identität signiert wird, mit der dasselbe APEX in den integrierten Partitionen signiert wird.
APEX-Benennungsrichtlinien
Um Namenskonflikte zwischen neuen APEX-Modulen zu vermeiden, wenn sich die Plattform weiterentwickelt, sollten Sie die folgenden Namensrichtlinien beachten:
com.android.*
- Für AOSP-APEX-Module reserviert. Nicht einzigartig 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>.*
- Für APEX-Dateien reserviert, die für ein bestimmtes Gerät (oder eine bestimmte Teilmenge von Geräten) eindeutig sind.
APEX-Manager
Der APEX-Manager (oder apexd
) ist ein eigenständiger nativer Prozess, der für das Überprüfen, Installieren und Deinstallieren von APEX-Dateien zuständig ist. Dieser Prozess wird gestartet und ist früh im Bootvorgang 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 sieht so aus:
- Eine APEX-Datei wird über eine Paketinstallations-App, ADB oder eine andere Quelle heruntergeladen.
- Der Paketmanager startet die Installation. Wenn der Paketmanager erkennt, dass es sich bei der Datei um ein APEX handelt, übergibt er die Steuerung an den APEX-Manager.
- Der APEX-Manager überprüft die APEX-Datei.
- 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.
- Der Anforderer der Installation erhält nach erfolgreicher Paketüberprüfung eine Broadcast-Nachricht.
- Damit die Installation fortgesetzt werden kann, muss das System neu gestartet werden.
Beim nächsten Start wird der APEX-Manager gestartet, die interne Datenbank gelesen und für jede aufgeführte APEX-Datei wird Folgendes ausgeführt:
- Überprüft die APEX-Datei.
- Erstellt ein Loopback-Gerät aus der APEX-Datei.
- Erstellt ein Device Mapper-Blockgerät auf dem Loopback-Gerät.
- Das Device Mapper-Blockgerät wird auf einem eindeutigen Pfad gemountet (z. B.
/apex/name@ver
).
Wenn alle in der internen Datenbank aufgeführten APEX-Dateien eingebunden sind, stellt der APEX-Manager einen Binder-Dienst für andere Systemkomponenten bereit, um Informationen zu den installierten APEX-Dateien abzufragen. Die anderen Systemkomponenten können beispielsweise die Liste der auf dem Gerät installierten APEX-Dateien oder den genauen Pfad abfragen, in dem ein bestimmter 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 nutzen, z. B. eine Paketinstallations-App, das Signaturdienstprogramm und den Paketmanager.
Die Datei AndroidManifest.xml
in einer APEX-Datei ist minimal und besteht aus dem Paket name
, versionCode
und optional targetSdkVersion
, minSdkVersion
und maxSdkVersion
für das detaillierte Targeting. 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 die folgenden Dateitypen:
- Gemeinsam genutzte native 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 davon ab, wie stabil die Definitionen der Schnittstellen für die Dateitypen sind.
Signaturoptionen
APEX-Dateien werden auf zwei Arten signiert. Zuerst wird die Datei apex_payload.img
(insbesondere der an apex_payload.img
angehängte vbmeta-Deskriptor) mit einem Schlüssel signiert.
Anschließend wird das gesamte APEX mit dem APK-Signaturschema v3 signiert. Bei diesem Vorgang werden zwei verschiedene Schlüssel verwendet.
Auf der Geräteseite ist 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-Dateien zu überprüfen, die installiert werden sollen. Jedes APEX muss mit unterschiedlichen Schlüsseln signiert werden. Dies wird sowohl zur Build-Zeit als auch zur Laufzeit erzwungen.
APEX in integrierten Partitionen
APEX-Dateien können sich in integrierten Partitionen wie /system
befinden. Die Partition ist bereits über dm-verity gesichert, sodass die APEX-Dateien direkt über das Loopback-Gerät bereitgestellt werden.
Wenn ein APEX in einer integrierten Partition vorhanden ist, kann er aktualisiert werden, indem ein APEX-Paket mit demselben Paketnamen und einem Versionscode bereitgestellt wird, der größer oder gleich ist. Das neue APEX wird in /data
gespeichert. Ähnlich wie bei APKs wird die bereits in der integrierten Partition vorhandene Version durch die neu installierte Version überschrieben. Im Gegensatz zu APKs wird die neu installierte Version des APEX jedoch erst nach dem Neustart aktiviert.
Kernel-Anforderungen
Damit APEX-Mainline-Module auf einem Android-Gerät unterstützt werden, sind die folgenden Linux-Kernel-Funktionen erforderlich: der Loopback-Treiber und dm-verity. Der Loopback-Treiber hängt das Dateisystem-Image in ein APEX-Modul ein und dm-verity prü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-Hauptmodule werden auf Geräten mit Kernel-Version 4.4 oder höher unterstützt. Auf neuen Geräten, die bei Markteinführung Android 10 oder höher nutzen, muss die Kernelversion 4.9 oder höher verwendet werden, um APEX-Module zu unterstützen.
Erforderliche Kernel-Patches
Die erforderlichen Kernel-Patches zur Unterstützung von APEX-Modulen sind im gemeinsamen Android-Baum enthalten. Wenn Sie die Patches zur Unterstützung von APEX erhalten möchten, verwenden Sie die aktuelle Version des gemeinsamen Android-Baums.
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 sollen. Um die erforderlichen Patches zu erhalten, wird ein Down-Merge vom android-4.4
-Branch dringend empfohlen. Im Folgenden finden Sie eine Liste der erforderlichen einzelnen Patches für die Kernelversion 4.4.
- UPSTREAM: loop: add ioctl for changing logical block size (4.4)
- BACKPORT: block/loop: set hw_sectors (4.4)
- UPSTREAM: loop: Add LOOP_SET_BLOCK_SIZE in compat ioctl (4.4)
- ANDROID: mnt: Fix next_descendent (4.4)
- ANDROID: mnt: remount should propagate to slaves of slaves (4.4)
- ANDROID: mnt: Propagate remount correctly (4.4)
- „ANDROID: dm verity: add minimum prefetch size“ zurücksetzen (4.4)
- UPSTREAM: loop: drop caches if offset or block_size are changed (4.4)
Kernel-Versionen 4.9/4.14/4.19
Die erforderlichen Patches für die Kernelversionen 4.9, 4.14 und 4.19 können aus dem Branch android-common
heruntergemergt werden.
Erforderliche Kernelkonfigurationsoptionen
In der folgenden Liste sind die Anforderungen an die Basiskonfiguration für die Unterstützung von APEX-Modulen aufgeführt, die in Android 10 eingeführt wurden. Die mit einem Sternchen (*) gekennzeichneten Elemente sind bestehende Anforderungen aus Android 9 und niedrigeren Versionen.
(*) 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 werdenloop.max_part
muss <= 8 sein
APEX erstellen
In diesem Abschnitt wird beschrieben, wie Sie mit dem Android-Build-System ein APEX erstellen.
Das folgende Beispiel zeigt 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 | Speicherort in APEX |
---|---|
Geteilte Fotogalerien | /lib und /lib64 (/lib/arm für übersetzte Arm-Architektur in x86) |
Ausführbare Dateien | /bin |
Java-Bibliotheken | /javalib |
Vorgefertigte | /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 beide Bibliotheken einbezogen, wenn nur libFoo
in der native_shared_libs
-Property aufgeführt ist.
Mehrere ABIs verarbeiten
Installieren Sie das Attribut native_shared_libs
für die primären und sekundären Binärschnittstellen (Application Binary Interfaces, ABIs) des Geräts. Wenn ein APEX auf Geräte mit einem einzelnen ABI (d. h. nur 32 Bit oder nur 64 Bit) ausgerichtet ist, werden nur Bibliotheken mit dem entsprechenden ABI installiert.
Installieren Sie die binaries
-Property 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 der Binärdatei installiert.
Wenn Sie die ABIs der nativen Bibliotheken und Binärdateien genauer steuern möchten, verwenden Sie die multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]
-Attribute.
first
: Entspricht dem primären ABI des Geräts. Dies ist die Standardeinstellung für Binärdateien.lib32
: Entspricht dem 32-Bit-ABI des Geräts, sofern unterstützt.lib64
: Entspricht der 64-Bit-ABI des Geräts, sofern es unterstützt wird.prefer32
: Entspricht dem 32-Bit-ABI des Geräts, sofern unterstützt. Wenn das 32-Bit-ABI nicht unterstützt wird, entspricht es dem 64-Bit-ABI.both
: Entspricht beiden ABIs. Dies ist die Standardeinstellung fürnative_shared_libraries
.
Die Attribute java
, libraries
und prebuilts
sind ABI-unabhängig.
Dieses Beispiel gilt für ein Gerät, das 32/64 Bit unterstützt und 32 Bit 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 jedes APEX mit unterschiedlichen Schlüsseln. Wenn ein neuer Schlüssel erforderlich ist, erstellen Sie ein Schlüsselpaar aus öffentlichem und privatem Schlüssel und ein apex_key
-Modul. Verwenden Sie das Attribut key
, um das APEX mit dem Schlüssel zu signieren. Der öffentliche Schlüssel wird automatisch mit dem Namen avb_pubkey
in den APEX aufgenommen.
# create an rsa key pairopenssl genrsa -out foo.pem 4096
# extract the public key from the key pairavbtool extract_public_key --key foo.pem --output foo.avbpubkey
# in Android.bpapex_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, ist im APEX enthalten. Zur Laufzeit prüft apexd
den APEX mit einem öffentlichen Schlüssel mit derselben ID auf dem Gerät.
APEX-Signierung
APEX-Dateien werden auf dieselbe Weise signiert 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 ein APEX auf Dateiebene signieren möchten, legen Sie das Attribut 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
befindet. Wenn kein Flag festgelegt ist, wird standardmäßig der Pfadbuild/target/product/security/testkey
verwendet. <name>
: Das APEX ist mit dem<name>
-Zertifikat im selben Verzeichnis wiePRODUCT_DEFAULT_DEV_CERTIFICATE
signiert.:<name>
: Das APEX ist mit dem Zertifikat signiert, das durch das Soong-Modul mit dem Namen<name>
definiert wird. Das Zertifikatsmodul kann so definiert werden:
android_app_certificate {
name: "my_key_name",
certificate: "dir/cert",
// this will use dir/cert.x509.pem (the cert) and dir/cert.pk8 (the private key)
}
APEX installieren
Verwenden Sie ADB, um ein APEX zu installieren.
adb install apex_file_name
adb reboot
Wenn supportsRebootlessUpdate
in apex_manifest.json
auf true
gesetzt ist und der aktuell installierte APEX nicht verwendet wird (z. B. alle darin enthaltenen Dienste wurden beendet), kann mit dem Flag --force-non-staged
ein neuer APEX ohne Neustart installiert werden.
adb install --force-non-staged apex_file_name
APEX verwenden
Nach dem Neustart wird das APEX-Paket im Verzeichnis /apex/<apex_name>@<version>
bereitgestellt. Es können mehrere Versionen desselben APEX gleichzeitig eingebunden werden.
Unter den Mount-Pfaden wird der Pfad, der der neuesten Version entspricht, unter /apex/<apex_name>
bind-gemountet.
Clients können den bind-gemounteten Pfad verwenden, um Dateien aus APEX zu lesen oder auszuführen.
APEX-Pakete werden in der Regel so verwendet:
- Ein OEM oder ODM lädt beim Versand des Geräts ein APEX unter
/system/apex
vor. - Auf Dateien im APEX wird über den Pfad
/apex/<apex_name>/
zugegriffen. - Wenn eine aktualisierte Version des APEX in
/data/apex
installiert wird, verweist der Pfad nach dem Neustart auf den neuen APEX.
Dienst mit einem APEX aktualisieren
So aktualisieren Sie einen Dienst mit einem APEX:
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
Erstellen Sie eine neue
.rc
-Datei für den aktualisierten Dienst. Verwenden Sie die Optionoverride
, 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 Datei .rc
eines APEX definiert werden. Aktionsauslöser werden in APEXes nicht unterstützt.
Wenn ein als aktualisierbar gekennzeichneter Dienst vor der Aktivierung der APEX-Dateien gestartet wird, verzögert sich der Start, bis die Aktivierung der APEX-Dateien abgeschlossen ist.
System für APEX-Updates konfigurieren
Legen Sie das folgende Systemattribut auf true
fest, um APEX-Dateiaktualisierungen 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
Bei älteren Geräten ist es manchmal unmöglich oder nicht praktikabel, den alten Kernel zu aktualisieren, um APEX vollständig zu unterstützen. Der Kernel wurde beispielsweise ohne CONFIG_BLK_DEV_LOOP=Y
erstellt, was für das Bereitstellen des Dateisystem-Images in einem APEX entscheidend ist.
Ein zusammengefasstes APEX ist ein speziell entwickelter APEX, der auf Geräten mit einem Legacy-Kernel aktiviert werden kann. Dateien in einem zusammengeführten APEX werden direkt in einem Verzeichnis unter der integrierten Partition installiert. Beispiel: lib/libFoo.so
in einem vereinfachten APEX-my.apex
wird in /system/apex/my.apex/lib/libFoo.so
installiert.
Beim Aktivieren eines zusammengeführten APEX ist das Loop-Gerät nicht beteiligt. Das gesamte Verzeichnis /system/apex/my.apex
wird 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. Zusammengeführte APEX-Module können nur über ein reguläres OTA-Update aktualisiert werden.
Die Standardkonfiguration ist „Flattened APEX“. Das bedeutet, dass alle APEX-Dateien standardmäßig zusammengeführt werden, sofern Sie Ihr Gerät nicht explizit so konfigurieren, dass nicht zusammengeführte APEX-Dateien erstellt werden, um APEX-Updates zu unterstützen (wie oben beschrieben).
Die Verwendung von komprimierten und nicht komprimierten APEX-Dateien auf einem Gerät wird NICHT unterstützt. APEX-Dateien auf einem Gerät müssen entweder alle nicht zusammengeführt oder alle zusammengeführt sein.
Das ist besonders wichtig, wenn vorab signierte APEX-Prebuilts für Projekte wie Mainline ausgeliefert werden. APEX-Dateien, die nicht vorab signiert sind (d. h. aus dem Quellcode erstellt wurden), sollten ebenfalls nicht zusammengeführt und mit den richtigen Schlüsseln signiert werden. Das Gerät sollte von updatable_apex.mk
erben, wie unter Dienst mit einem APEX aktualisieren beschrieben.
Komprimierte APEX-Dateien
In Android 12 und höher wird APEX-Komprimierung verwendet, um den Speicherbedarf von aktualisierbaren APEX-Paketen zu verringern. Nachdem ein Update für ein APEX installiert wurde, wird die vorinstallierte Version zwar nicht mehr verwendet, belegt aber weiterhin denselben Speicherplatz. Der belegte Speicherplatz bleibt weiterhin 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. Unter Android 12 und höher wird der DEFLATE-Algorithmus zur ZIP-Komprimierung verwendet.
Die Komprimierung bietet keine Optimierung für Folgendes:
Bootstrap-APEXs, die sehr früh im Bootvorgang eingebunden werden müssen.
APEX-Module, die nicht aktualisiert werden können. Die Komprimierung ist nur dann von Vorteil, wenn eine aktualisierte Version eines APEX auf der Partition
/data
installiert ist. Eine vollständige Liste der aktualisierbaren APEX-Module finden Sie auf der Seite Modulare Systemkomponenten.APEX-Module für dynamische gemeinsam genutzte Bibliotheken. Da
apexd
immer beide Versionen solcher APEX-Module (vorinstalliert und aktualisiert) aktiviert, bringt das Komprimieren keinen Mehrwert.
Komprimiertes APEX-Dateiformat
Dies ist das Format einer komprimierten APEX-Datei.
Abbildung 2: Komprimiertes APEX-Dateiformat
Auf der obersten Ebene ist eine komprimierte APEX-Datei eine ZIP-Datei, die die ursprüngliche APEX-Datei in dekomprimierter Form mit einem Komprimierungsgrad von 9 und andere Dateien unkomprimiert enthält.
Eine APEX-Datei besteht aus vier Dateien:
original_apex
: mit Komprimierungsstufe 9 entpackt Dies ist die ursprüngliche, unkomprimierte APEX-Datei.apex_manifest.pb
: Nur gespeichertAndroidManifest.xml
: Nur gespeichertapex_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 Tool apex_compression_tool.py
unter system/apex/tools
erstellt werden.
Im Build-System sind mehrere Parameter für die APEX-Komprimierung verfügbar.
In Android.bp
wird gesteuert, ob eine APEX-Datei komprimierbar ist, indem das Attribut compressible
verwendet wird:
apex {
name: "apex.test",
manifest: "apex_manifest.json",
file_contexts: "file_contexts",
compressible: true,
}
Ein PRODUCT_COMPRESSED_APEX
-Produkt-Flag steuert, ob ein aus dem Quellcode erstelltes Systemimage komprimierte APEX-Dateien enthalten muss.
Für lokale Tests können Sie erzwingen, dass APEX-Dateien komprimiert werden, indem Sie OVERRIDE_PRODUCT_COMPRESSED_APEX=
auf true
setzen.
Vom Build-System generierte komprimierte APEX-Dateien haben die Endung .capex
.
Die Erweiterung erleichtert die Unterscheidung zwischen komprimierten und unkomprimierten Versionen einer APEX-Datei.
Unterstützte Komprimierungsalgorithmen
Android 12 unterstützt nur die Deflate-Zip-Komprimierung.
Komprimierte APEX-Datei beim Booten aktivieren
Bevor ein komprimiertes APEX aktiviert werden kann, wird die Datei original_apex
darin in das Verzeichnis /data/apex/decompressed
dekomprimiert. Die dekomprimierte APEX-Datei wird mit dem Verzeichnis /data/apex/active
verknüpft.
Das folgende Beispiel veranschaulicht den oben beschriebenen Prozess.
Stellen Sie sich /system/apex/com.android.foo.capex
als komprimiertes APEX mit dem aktivierten versionCode 37 vor.
- Die Datei
original_apex
im Ordner/system/apex/com.android.foo.capex
wird in/data/apex/decompressed/com.android.foo@37.apex
dekomprimiert. restorecon /data/apex/decompressed/com.android.foo@37.apex
wird ausgeführt, um zu prüfen, ob es ein korrektes SELinux-Label hat.- Es werden Überprüfungen für
/data/apex/decompressed/com.android.foo@37.apex
durchgeführt, um die Gültigkeit zu bestätigen:apexd
prüft den in/data/apex/decompressed/com.android.foo@37.apex
enthaltenen öffentlichen Schlüssel, um zu bestätigen, dass er mit dem in/system/apex/com.android.foo.capex
enthaltenen öffentlichen Schlüssel übereinstimmt. - 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. - 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 eine komprimierte APEX-Datei mit einem höheren Versionsniveau als das auf einem Gerät aktive enthalten kann, muss vor dem Neustart eines Geräts zum Anwenden eines OTA-Updates ein bestimmter freier Speicherplatz reserviert werden.
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. Damit kann überprüft werden, 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 durchapexd
zum Dekomprimieren komprimierter APEX-Dateien im OTA-Paket.
Bei einem A/B-OTA-Update versucht apexd
, die Dekomprimierung im Hintergrund als Teil der OTA-Routine nach der Installation durchzuführen. Wenn die Dekomprimierung fehlschlägt, führt apexd
die Dekomprimierung während des Bootvorgangs durch, bei dem das OTA-Update angewendet wird.
Alternativen, die bei der Entwicklung von APEX in Betracht gezogen wurden
Hier sind einige Optionen, die bei der Entwicklung des APEX-Dateiformats in AOSP berücksichtigt wurden, und die Gründe, warum sie entweder einbezogen 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 beeinträchtigen. 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 jeden E/A-Vorgang geschützt ist. Manipulationen an Systemkomponenten müssen entweder verboten oder erkennbar sein, damit das Gerät den Start verweigern kann, wenn es manipuliert wurde.
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. Jegliche Änderungen an den Dateien sind auch nach dem Mounten der Partitionen verboten. Um den Dateien das gleiche Sicherheitsniveau 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 Partition /data
anfällig für unbeabsichtigte Änderungen, die nach der Bestätigung und Installation vorgenommen werden.
Tatsächlich ist die /data
-Partition auch durch Verschlüsselungsebenen wie dm-crypt geschützt. Dies bietet zwar einen gewissen Schutz vor Manipulationen, der primäre Zweck ist jedoch der Datenschutz, nicht die Integrität. Wenn ein Angreifer Zugriff auf die /data
-Partition erhält, gibt es keinen weiteren Schutz. Dies ist wiederum ein Rückschritt im Vergleich dazu, dass sich jede Systemkomponente 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 zu /apex umleiten
Auf Systemkomponentendateien, die in einem APEX verpackt sind, kann über neue Pfade wie /apex/<name>/lib/libfoo.so
zugegriffen werden. Wenn die Dateien Teil der /system
-Partition waren, konnte über Pfade wie /system/lib/libfoo.so
darauf zugegriffen werden. Ein Client einer APEX-Datei (andere APEX-Dateien oder die Plattform) muss die neuen Pfade verwenden. Möglicherweise müssen Sie vorhandenen Code aufgrund der Pfadänderung aktualisieren.
Eine Möglichkeit, die Pfadänderung zu vermeiden, besteht darin, die Dateiinhalte in einer APEX-Datei auf die /system
-Partition zu legen. Das Android-Team hat sich jedoch dagegen entschieden, Dateien auf die /system
-Partition zu legen, da dies die Leistung beeinträchtigen könnte, wenn die Anzahl der Dateien, die überlagert werden (möglicherweise sogar übereinander), zunimmt.
Eine weitere Option 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
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 Bionic beispielsweise statisch, wodurch die Funktionen implementiert werden.
In solchen Fällen werden diese Apps nicht weitergeleitet.