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.
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.
- Eine APEX-Datei wird über eine Paketinstallations-App, ADB oder eine andere Quelle heruntergeladen.
- 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.
- Der APEX-Manager überprüft die APEX-Datei.
- 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.
- Der Installationsanforderer erhält nach erfolgreicher Paketüberprüfung eine Rundsendung.
- Um die Installation fortzusetzen, muss das System neu gestartet werden.
Beim nächsten Start startet der APEX-Manager, liest die interne Datenbank und führt für jede aufgelistete APEX-Datei Folgendes aus:
- Ü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.
- 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ürnative_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 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, 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äßigbuild/target/product/security/testkey
. -
<name>
: Der APEX wird mit dem Zertifikat<name>
im selben Verzeichnis wiePRODUCT_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:
- Ein OEM oder ODM lädt bei Auslieferung des Geräts einen APEX unter
/system/apex
vor. - Der Zugriff auf Dateien im APEX erfolgt über den Pfad
/apex/<apex_name>/
. - 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:
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
Erstellen Sie eine neue
.rc
Datei für den aktualisierten Dienst. Verwenden Sie dieoverride
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.
Abgeflachtes 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.
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.
- Die Datei
original_apex
in/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 überprüfen, ob es ein korrektes SELinux-Label hat. - 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. - 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 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 durchapexd
zum Dekomprimieren komprimierter APEX-Dateien im OTA-Paket.
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 in einer APEX-Datei auf der /system
Partition zu überlagern, hat das Android-Team beschlossen, Dateien auf der /system
Partition nicht 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.