Formato file APEX

Il formato container Android Pony EXpress (APEX) è stato introdotto in Android 10 e viene utilizzato nel flusso di installazione per i moduli di sistema di livello inferiore. Questo formato facilita gli aggiornamenti dei componenti di sistema che non rientrano nel modello di applicazione Android standard. Alcuni componenti di esempio sono servizi e librerie nativi, livelli di astrazione hardware (HAL), runtime (ART) e librerie di classi.

Il termine "APEX" può anche fare riferimento a un file APEX.

Background

Sebbene Android supporti gli aggiornamenti dei moduli che si adattano al modello di app standard (ad esempio servizi e attività) tramite app di installazione di pacchetti (come l'app Google Play Store), l'utilizzo di un modello simile per i componenti di sistema operativo di livello inferiore presenta i seguenti svantaggi:

  • I moduli basati su APK non possono essere utilizzati all'inizio della sequenza di avvio. Il gestore dei pacchetti è il repository centrale delle informazioni sulle app e può essere avviato solo dal gestore delle attività, che diventa disponibile in una fase successiva della procedura di avvio.
  • Il formato APK (in particolare il file manifest) è progettato per le app per Android e i moduli di sistema non sono sempre adatti.

Design

Questa sezione descrive la progettazione di alto livello del formato file APEX e il gestore APEX, un servizio che gestisce i file APEX.

Per ulteriori informazioni sul motivo per cui è stata selezionata questa progettazione per APEX, consulta Alternative prese in considerazione durante lo sviluppo di APEX.

Formato APEX

Questo è il formato di un file APEX.

Formato file APEX

Figura 1. Formato file APEX

Al livello superiore, un file APEX è un file ZIP in cui i file vengono archiviati non compressi e posizionati a limiti di 4 kB.

I quattro file di un file APEX sono:

  • apex_manifest.json
  • AndroidManifest.xml
  • apex_payload.img
  • apex_pubkey

Il file apex_manifest.json contiene il nome e la versione del pacchetto, che identificano un file APEX. Si tratta di un buffer di protocollo ApexManifest in formato JSON.

Il file AndroidManifest.xml consente al file APEX di utilizzare strumenti e infrastrutture relativi all'APK, come ADB, PackageManager e alle app di installazione di pacchetti (come il Play Store). Ad esempio, il file APEX può utilizzare uno strumento esistente come aapt per ispezionare i metadati di base del file. Il file contiene il nome del pacchetto e informazioni sulla versione. Queste informazioni sono generalmente disponibili anche in: apex_manifest.json.

apex_manifest.json è consigliato rispetto a AndroidManifest.xml per il nuovo codice e per i sistemi che si occupano di APEX. AndroidManifest.xml potrebbe contenere ulteriori informazioni sul targeting utilizzabili dagli strumenti di pubblicazione delle app esistenti.

apex_payload.img è un'immagine del file system ext4 basata su dm-verity. L'immagine viene montata in fase di esecuzione tramite un dispositivo loopback. Nello specifico, l'albero hash e il blocco dei metadati vengono creati utilizzando la libreria libavb. Il payload del file system non viene analizzato (perché l'immagine deve essere montabile in posizione). I file normali sono inclusi nel file apex_payload.img.

apex_pubkey è la chiave pubblica utilizzata per firmare l'immagine del file system. In fase di esecuzione, questa chiave garantisce che l'APEX scaricato sia firmato con la stessa entità che firma lo stesso APEX nelle partizioni integrate.

Linee guida per la denominazione APEX

Per evitare conflitti di denominazione tra i nuovi APEX con i progressi della piattaforma, utilizza le seguenti linee guida per la denominazione:

  • com.android.*
    • Riservato per gli APEX AOSP. Non univoco per alcuna azienda o dispositivo.
  • com.<companyname>.*
    • Riservato a un'azienda. Potenzialmente usato da più dispositivi di quella azienda.
  • com.<companyname>.<devicename>.*
    • Riservato per gli APEX univoci per un dispositivo specifico (o un sottoinsieme di dispositivi).

Gestore APEX

APEX Manager (o apexd) è un processo nativo autonomo responsabile della verifica, dell'installazione e della disinstallazione dei file APEX. Questo processo viene avviato ed è pronto all'inizio della sequenza di avvio. I file APEX sono in genere preinstallati sul dispositivo in /system/apex. Il gestore APEX utilizza per impostazione predefinita questi pacchetti se non sono disponibili aggiornamenti.

La sequenza di aggiornamento di un APEX utilizza la classe PackageManager ed è la seguente.

  1. Un file APEX viene scaricato tramite un'app di installazione del pacchetto, ADB o un'altra fonte.
  2. Il gestore di pacchetti avvia la procedura di installazione. Una volta riconosciuto che il file è un APEX, il gestore del pacchetto trasferisce il controllo al gestore APEX.
  3. Il gestore APEX verifica il file APEX.
  4. Se il file APEX viene verificato, il database interno del gestore APEX viene aggiornato per indicare che il file APEX viene attivato al successivo avvio.
  5. Il richiedente dell'installazione riceve un broadcast al termine della verifica del pacchetto.
  6. Per continuare l'installazione è necessario riavviare il sistema.
  7. All'avvio successivo, il gestore APEX si avvia, legge il database interno ed effettua le seguenti operazioni per ogni file APEX elencato:

    1. Verifica il file APEX.
    2. Crea un dispositivo di loopback dal file APEX.
    3. Crea un dispositivo di blocco device Mapper sopra il dispositivo di loopback.
    4. Monta il dispositivo di blocco del mapper dei dispositivi su un percorso univoco (ad esempio, /apex/name@ver).

Una volta montati tutti i file APEX elencati nel database interno, il gestore APEX fornisce un servizio binder per gli altri componenti di sistema per eseguire query sulle informazioni sui file APEX installati. Ad esempio, gli altri componenti di sistema possono interrogare l'elenco dei file APEX installati nel dispositivo o interrogare il percorso esatto in cui è montato un APEX specifico, in modo da poter accedere ai file.

I file APEX sono file APK

I file APEX sono file APK validi perché sono archivi ZIP firmati (che utilizzano lo schema di firma dell'APK) contenenti un file AndroidManifest.xml. In questo modo, i file APEX possono utilizzare l'infrastruttura per i file APK, ad esempio un'app di installazione del pacchetto, l'utility di firma e il gestore del pacchetto.

Le dimensioni minime del file AndroidManifest.xml all'interno di un file APEX sono costituite dai pacchetti name, versionCode e targetSdkVersion, minSdkVersion e maxSdkVersion facoltative per un targeting granulare. Queste informazioni consentono di pubblicare i file APEX tramite canali esistenti, come le app di installazione di pacchetti e ADB.

Tipi di file supportati

Il formato APEX supporta i seguenti tipi di file:

  • Libreria condivisa nativa
  • Eseguibili nativi
  • File JAR
  • File di dati
  • File di configurazione

Ciò non significa che APEX possa aggiornare tutti questi tipi di file. La possibilità di aggiornare un tipo di file dipende dalla piattaforma e da quanto sono stabili le definizioni delle interfacce per i tipi di file.

Opzioni di firma

I file APEX sono firmati in due modi. Innanzitutto, il file apex_payload.img (in particolare, il descrittore vbmeta aggiunto a apex_payload.img) è firmato con una chiave. Successivamente, l'intero APEX viene firmato utilizzando lo schema di firma dell'APK v3. In questo processo vengono utilizzate due chiavi diverse.

Sul lato del dispositivo, viene installata una chiave pubblica corrispondente alla chiave privata utilizzata per firmare il descrittore vbmeta. Il gestore APEX utilizza la chiave pubblica per verificare gli APEX di cui è richiesta l'installazione. Ogni APEX deve essere firmato con chiavi diverse e viene applicato sia in fase di build che in fase di runtime.

APEX nelle partizioni integrate

I file APEX possono trovarsi in partizioni integrate come /system. La partizione è già su dm-verity, quindi i file APEX vengono montati direttamente sul dispositivo loopback.

Se in una partizione integrata è presente un APEX, questo può essere aggiornato fornendo un pacchetto APEX con lo stesso nome e un codice di versione maggiore o uguale. Il nuovo APEX è archiviato in /data e, come per gli APK, la versione appena installata è oscurata da quella già presente nella partizione integrata. Tuttavia, a differenza degli APK, la versione appena installata dell'APEX viene attivata solo dopo il riavvio.

Requisiti del kernel

Per supportare i moduli APEX mainline su un dispositivo Android, sono necessarie le seguenti funzionalità del kernel Linux: il driver di loopback e dm-verity. Il driver loopback monta l'immagine del file system in un modulo APEX e dm-verity verifica il modulo APEX.

Le prestazioni del driver di loopback e di dm-verity sono importanti per ottenere buone prestazioni del sistema quando si utilizzano i moduli APEX.

Versioni del kernel supportate

I moduli principali APEX sono supportati sui dispositivi che utilizzano versioni del kernel 4.4 o superiori. I nuovi dispositivi che vengono lanciati con Android 10 o versioni successive devono usare il kernel versione 4.9 o successiva per supportare i moduli APEX.

Patch del kernel richieste

Le patch del kernel richieste per il supporto dei moduli APEX sono incluse nell'albero comune di Android. Per fare in modo che le patch supportino APEX, utilizza la versione più recente del tree comune di Android.

Versione del kernel 4.4

Questa versione è supportata solo per i dispositivi di cui è stato eseguito l'upgrade da Android 9 ad Android 10 e che vogliono supportare i moduli APEX. Per ottenere le patch richieste, si consiglia vivamente di eseguire un'unione dinamica dal ramo android-4.4. Di seguito è riportato un elenco delle singole patch necessarie per la versione 4.4 del kernel.

  • UPSTREAM: loop: aggiungi ioctl per modificare la dimensione del blocco logico (4,4)
  • BACKPORT: block/loop: imposta hw_sectors (4,4)
  • UPSTREAM: loop: aggiungi LOOP_SET_BLOCK_SIZE in ioctl compat (4.4)
  • ANDROID: mnt: correzione di next_descendent (4.4)
  • ANDROID: mnt: il montaggio deve essere propagato ai dispositivi slave di slave (4.4)
  • ANDROID: mnt: propaga il montaggio nuovamente correttamente (4.4)
  • Ripristina "ANDROID: dm verity: add minimum prefetch size" (4.4)
  • UPSTREAM: loop: elimina le cache se vengono modificati gli offset o le dimensioni block_size (4.4)

Versioni del kernel 4.9/4.14/4.19

Per ottenere le patch necessarie per le versioni del kernel 4.9/4.14/4.19, esegui il merge dal ramo android-common.

Opzioni di configurazione del kernel richieste

Il seguente elenco mostra i requisiti di configurazione di base per supportare i moduli APEX introdotti in Android 10. Gli elementi contrassegnati con un asterisco (*) sono requisiti esistenti di Android 9 e versioni precedenti.

(*) 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

Requisiti dei parametri della riga di comando del kernel

Per supportare APEX, assicurati che i parametri della riga di comando del kernel soddisfino i seguenti requisiti:

  • loop.max_loop NON deve essere impostato
  • loop.max_part deve essere <= 8

Creare un APEX

Questa sezione descrive come compilare un file APEX utilizzando il sistema di compilazione Android. Di seguito è riportato un esempio di Android.bp per un APEX denominato 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",
}

Esempio di apex_manifest.json:

{
  "name": "com.android.example.apex",
  "version": 1
}

Esempio di file_contexts:

(/.*)?           u:object_r:system_file:s0
/sub(/.*)?       u:object_r:sub_file:s0
/sub/file3       u:object_r:file3_file:s0

Tipi di file e posizioni in APEX

Tipo di file Posizione in APEX
Raccolte condivise /lib e /lib64 (/lib/arm per arm tradotto in x86)
Eseguibili /bin
Librerie Java /javalib
Predefiniti /etc

Dipendenze transitive

I file APEX includono automaticamente le dipendenze transitive delle librerie o degli eseguibili condivisi nativi. Ad esempio, se libFoo dipende da libBar, le due librerie sono incluse quando nella proprietà native_shared_libs è elencato solo libFoo.

Gestire più ABI

Installa la proprietà native_shared_libs sia per le ABI (Application Binary Interface) principali che secondarie del dispositivo. Se un APEX ha come target dispositivi con un singolo ABI (ovvero solo 32 bit o solo 64 bit), vengono installate solo le librerie con l'ABI corrispondente.

Installa la proprietà binaries solo per l'ABI principale del dispositivo come descritto di seguito:

  • Se il dispositivo è solo a 32 bit, viene installata solo la variante a 32 bit del file binario.
  • Se il dispositivo è solo a 64 bit, viene installata solo la variante a 64 bit del file binario.

Per aggiungere un controllo granulare sulle ABI delle librerie native e dei programmi binari, utilizza le proprietà multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries].

  • first: corrisponde all'ABI principale del dispositivo. Questo è il valore predefinito per i file binari.
  • lib32: corrisponde all'ABI a 32 bit del dispositivo, se supportato.
  • lib64: corrisponde all'ABI a 64 bit del dispositivo supportato.
  • prefer32: corrisponde all'ABI a 32 bit del dispositivo, se supportata. Se l'ABI a 32 bit non è supportato, corrisponde all'ABI a 64 bit.
  • both: corrisponde a entrambi gli ABI. Questa è l'impostazione predefinita per native_shared_libraries.

Le proprietà java, libraries e prebuilts sono indipendenti dall'ABI.

Questo esempio è per un dispositivo che supporta 32/64 e non preferisce 32:

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
        },
    },
}

Firma vbmeta

Firma ogni APEX con chiavi diverse. Quando è necessaria una nuova chiave, crea una coppia di chiavi pubblica-privata e un modulo apex_key. Utilizza la proprietà key per firmare l'APEX utilizzando la chiave. La chiave pubblica viene inclusa automaticamente in APEX con il nome avb_pubkey.

# 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",
}

Nell'esempio precedente, il nome della chiave pubblica (foo) diventa l'ID della chiave. L'ID della chiave utilizzata per firmare un APEX è scritto nell'APEX. In fase di esecuzione, apexd verifica l'APEX utilizzando una chiave pubblica con lo stesso ID nel dispositivo.

Firma APEX

Firma gli APEX nello stesso modo in cui firmi gli APK. Firma gli APEX due volte: una volta per il mini file system (file apex_payload.img) e una volta per l'intero file.

Per firmare un codice APEX a livello di file, imposta la proprietà certificate in uno di questi tre modi:

  • Non impostato: se non viene impostato alcun valore, l'APEX viene firmato con il certificato che si trova all'indirizzo PRODUCT_DEFAULT_DEV_CERTIFICATE. Se non viene impostato alcun flag, il percorso predefinito è build/target/product/security/testkey.
  • <name>: APEX è firmato con il certificato <name> nella stessa directory di PRODUCT_DEFAULT_DEV_CERTIFICATE.
  • :<name>: l'APEX è firmato con il certificato definito dal modulo Soong denominato <name>. Il modulo del certificato può essere definito come segue.
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)
}

Installa un APEX

Per installare un APEX, utilizza ADB.

adb install apex_file_name
adb reboot

Se supportsRebootlessUpdate è impostato su true in apex_manifest.json e l'APEX attualmente installato non è in uso (ad esempio, tutti i servizi in esso contenuti sono stati arrestati), è possibile installare un nuovo APEX senza un riavvio con il flag --force-non-staged.

adb install --force-non-staged apex_file_name

Utilizzare un APEX

Dopo il riavvio, l'APEX viene montato nella directory /apex/<apex_name>@<version>. È possibile montare più versioni dello stesso APEX contemporaneamente. Tra i percorsi di montaggio, quello che corrisponde alla versione più recente è montato in modo da associarlo a /apex/<apex_name>.

I client possono utilizzare il percorso montato tramite il binding per leggere o eseguire file da APEX.

Gli APEX vengono generalmente utilizzati come segue:

  1. Un OEM o un ODM precarica un APEX in /system/apex quando il dispositivo viene spedito.
  2. I file nell'APEX sono accessibili tramite il percorso /apex/<apex_name>/.
  3. Quando in /data/apex viene installata una versione aggiornata dell'APEX, il percorso rimanda al nuovo APEX dopo il riavvio.

Aggiornare un servizio con un APEX

Per aggiornare un servizio utilizzando un'espressione APEX:

  1. Contrassegna il servizio nella partizione di sistema come aggiornabile. Aggiungi l'opzione updatable alla definizione del servizio.

    /system/etc/init/myservice.rc:
    
    service myservice /system/bin/myservice
        class core
        user system
        ...
        updatable
    
  2. Crea un nuovo file .rc per il servizio aggiornato. Utilizza l'opzione override per ridefinire il servizio esistente.

    /apex/my.apex/etc/init.rc:
    
    service myservice /apex/my.apex/bin/myservice
        class core
        user system
        ...
        override
    

Le definizioni del servizio possono essere definite solo nel file .rc di un APEX. Gli attivatori di azioni non sono supportati negli APEX.

Se un servizio contrassegnato come aggiornabile viene avviato prima dell'attivazione degli APEX, l'avvio viene ritardato fino al completamento dell'attivazione degli APEX.

Configura il sistema per supportare gli aggiornamenti APEX

Imposta la seguente proprietà di sistema su true per supportare gli aggiornamenti dei file APEX.

<device.mk>:

PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true

BoardConfig.mk:
TARGET_FLATTEN_APEX := false

o semplicemente

<device.mk>:

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

APEX appiattito

Per i dispositivi legacy, a volte è impossibile o impossibile aggiornare il kernel precedente per supportare completamente APEX. Ad esempio, il kernel potrebbe essere stato compilato senza CONFIG_BLK_DEV_LOOP=Y, che è fondamentale per montare l'immagine del file system all'interno di un APEX.

L'APEX flat è un APEX creato appositamente che può essere attivato su dispositivi con un kernel legacy. I file in un APEX bidimensionale vengono installati direttamente in una directory all'interno della partizione integrata. Ad esempio, lib/libFoo.so in un progetto APEX spianatomy.apex è installato in /system/apex/my.apex/lib/libFoo.so.

L'attivazione di un APEX appiattito non coinvolge il dispositivo di loop. L'intera directory /system/apex/my.apex è montata direttamente in associazione a /apex/name@ver.

Gli APEX compressi non possono essere aggiornati scaricando versioni aggiornate degli APEX dalla rete perché gli APEX scaricati non possono essere suddivisi. Gli APEX bidimensionali possono essere aggiornati soltanto tramite una normale OTA.

La configurazione predefinita è APEX bidimensionale. Ciò significa che per impostazione predefinita tutti gli APEX sono appiattiti, a meno che tu non configuri esplicitamente il dispositivo per generare APEX non appiattiti in modo da supportare gli aggiornamenti APEX (come spiegato sopra).

NON è supportato l'unione di APEX bidimensionali e non appiattiti in un dispositivo. Gli APEX di un dispositivo devono essere tutti non appiattiti o tutti appiattiti. Questo è particolarmente importante quando si spediscono soluzioni APEX pre-firmate predefinite per progetti come Mainline. Anche gli APEX non prefirmati (ovvero creati dall'origine) non devono essere semplificati e firmati con chiavi appropriate. Il dispositivo deve ereditare da updatable_apex.mk come spiegato in Aggiornamento di un servizio con un APEX.

APEX compressi

Android 12 e versioni successive includono la compressione APEX per ridurre l'impatto sullo spazio di archiviazione dei pacchetti APEX aggiornabili. Dopo aver installato un aggiornamento a un APEX, anche se la sua versione preinstallata non viene più utilizzata, occupa comunque la stessa quantità di spazio. Lo spazio occupato rimane non disponibile.

La compressione APEX riduce al minimo questo impatto sullo spazio di archiviazione utilizzando un insieme di file APEX altamente compressi su partizioni di sola lettura (ad esempio la partizione /system). Android 12 e versioni successive utilizzano un algoritmo di compressione ZIP DEFLATE.

La compressione non fornisce l'ottimizzazione per quanto segue:

  • APEX di bootstrap che devono essere montati molto all'inizio della sequenza di avvio.

  • APEX non aggiornabili. La compressione è utile solo se è installata una versione aggiornata di un APEX nella partizione /data. Un elenco completo degli APEX aggiornabili è disponibile nella pagina Componenti di sistema modulari.

  • APEX libs condivisi dinamici. Poiché apexd attiva sempre entrambe le versioni degli APEX (preinstallati e con upgrade eseguito), la loro compressione non aggiunge valore.

Formato file APEX compresso

Formato di file APEX compresso.

Il diagramma mostra il formato di un file APEX compresso

Figura 2. Formato file APEX compresso

Al livello superiore, un file APEX compresso è un file ZIP contenente il file Apex originale in forma sgonfiata con un livello di compressione pari a 9 e con altri file archiviati non compressi.

Un file APEX è formato da quattro file:

  • original_apex: sgonfiato con livello di compressione pari a 9 Questo è il file APEX originale non compresso.
  • apex_manifest.pb: solo memorizzato
  • AndroidManifest.xml: solo archiviati
  • apex_pubkey: solo memorizzato

I file apex_manifest.pb, AndroidManifest.xml e apex_pubkey sono copie dei file corrispondenti in original_apex.

Compila il codice APEX compresso

Puoi creare l'APEX compresso utilizzando lo strumento apex_compression_tool.py all'indirizzo system/apex/tools.

Nel sistema di compilazione sono disponibili diversi parametri relativi alla compressione APEX.

In Android.bp, se un file APEX è comprimibile è controllato dalla proprietà compressible:

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

Un flag prodotto PRODUCT_COMPRESSED_APEX controlla se un'immagine di sistema compilata da sorgente deve contenere file APEX compressi.

Per la sperimentazione locale, puoi forzare una build a comprimere gli APEX impostando OVERRIDE_PRODUCT_COMPRESSED_APEX= su true.

I file APEX compressi generati dal sistema di compilazione hanno l'estensione .capex. L'estensione consente di distinguere più facilmente le versioni compresse e non compresse di un file APEX.

Algoritmi di compressione supportati

Android 12 supporta solo la compressione deflate-zip.

Attiva un file APEX compresso durante l'avvio

Prima che un file APEX compresso possa essere attivato, il file original_apex al suo interno viene decompresso nella directory /data/apex/decompressed. Il file APEX decompressato risultante è collegato in modo fisso alla directory /data/apex/active.

Considera l'esempio che segue per mostrare la procedura descritta in precedenza.

Considera /system/apex/com.android.foo.capex come un APEX compresso attivato, con il codice versione 37.

  1. Il file original_apex all'interno di /system/apex/com.android.foo.capex è decompresso in /data/apex/decompressed/com.android.foo@37.apex.
  2. restorecon /data/apex/decompressed/com.android.foo@37.apex viene eseguita per verificare che abbia un'etichetta SELinux corretta.
  3. I controlli di verifica vengono eseguiti il giorno /data/apex/decompressed/com.android.foo@37.apex per verificarne la validità: apexd controlla la chiave pubblica nel bundle in /data/apex/decompressed/com.android.foo@37.apex per verificare che sia uguale a quella del bundle in /system/apex/com.android.foo.capex.
  4. Il file /data/apex/decompressed/com.android.foo@37.apex è collegato in modo fisso alla directory /data/apex/active/com.android.foo@37.apex.
  5. La normale logica di attivazione per i file APEX non compressi viene eseguita su /data/apex/active/com.android.foo@37.apex.

Interazione con OTA

I file APEX compressi hanno implicazioni sulla distribuzione e sull'applicazione OTA. Poiché un aggiornamento OTA potrebbe contenere un file APEX compresso con un livello di versione superiore a quello attivo su un dispositivo, è necessario riservare una certa quantità di spazio libero prima di riavviare un dispositivo per applicare un aggiornamento OTA.

Per supportare il sistema OTA, apexd espone queste due API binder:

  • calculateSizeForCompressedApex: calcola le dimensioni necessarie per decomprimere i file APEX in un pacchetto OTA. Questo può essere utilizzato per verificare che su un dispositivo sia presente spazio sufficiente prima del download di un aggiornamento OTA.
  • reserveSpaceForCompressedApex: riserva spazio sul disco, che potrà essere utilizzato in futuro da apexd per decomprimere i file APEX compressi all'interno del pacchetto OTA.

Nel caso di un aggiornamento OTA A/B, apexd tenta la decompressione in background nell'ambito della routine OTA post-installazione. Se la decompressione non va a buon fine, apexd la esegue durante l'avvio che applica l'aggiornamento OTA.

Alternative prese in considerazione durante lo sviluppo di APEX

Ecco alcune opzioni che AOSP ha preso in considerazione durante la progettazione del formato file APEX e perché sono state incluse o escluse.

Normali sistemi di gestione dei pacchetti

Le distribuzioni Linux dispongono di sistemi di gestione dei pacchetti potenti, maturi e affidabili, come dpkg e rpm. Tuttavia, non sono stati adottati per APEX perché non possono proteggere i pacchetti dopo l'installazione. La verifica viene eseguita soltanto durante l'installazione dei pacchetti. Gli utenti malintenzionati possono compromettere l'integrità dei pacchetti installati, passando inosservati. Si tratta di una regressione per Android in cui tutti i componenti di sistema sono stati memorizzati in file system di sola lettura la cui integrità è protetta da dm-verity per ogni I/O. Qualsiasi manomissione dei componenti di sistema deve essere vietata o rilevabile in modo che il dispositivo possa rifiutarsi di avviarsi in caso di compromissione.

Dm-crypt per integrità

I file in un container APEX provengono da partizioni integrate (ad esempio, la partizione /system) protette da dm-verity, dove qualsiasi modifica ai file è vietata anche dopo il montaggio delle partizioni. Per fornire lo stesso livello di sicurezza ai file, tutti i file in un APEX vengono archiviati in un'immagine di sistema di file accoppiata a una struttura ad albero di hash e a un descrittore vbmeta. Senza dm-verity, un APEX nella partizione /data è vulnerabile alle modifiche indesiderate apportate dopo la verifica e l'installazione.

Di fatto, la partizione /data è anche protetta da livelli di crittografia come dm-crypt. Sebbene questo offra un certo livello di protezione contro le manomissioni, il suo scopo principale è la privacy, non l'integrità. Quando un utente malintenzionato ottiene l'accesso alla partizione /data, non può esserci ulteriore protezione e si tratta di nuovo di una regressione rispetto a ogni componente del sistema che si trova nella partizione /system. L'albero di hash all'interno di un file APEX, insieme a dm-verity, fornisce lo stesso livello di protezione dei contenuti.

Percorsi di reindirizzamento da /system a /apex

I file dei componenti di sistema pacchettizzati in un APEX sono accessibili tramite nuovi percorsi come/apex/<name>/lib/libfoo.so. Quando i file facevano parte della partizione /system, erano accessibili tramite percorsi come /system/lib/libfoo.so. Un client di un file APEX (altri file APEX o della piattaforma) deve utilizzare i nuovi percorsi. Potresti dover aggiornare il codice esistente a seguito della modifica del percorso.

Sebbene un modo per evitare la modifica del percorso sia sovrapporre i contenuti di un file APEX sulla partizione /system, il team Android ha deciso di non sovrapporre i file sulla partizione /system perché questo potrebbe influire sulle prestazioni con l'aumento del numero di file sovrapposti (possibilmente anche impilati uno dopo l'altro).

Un'altra opzione era quella di rubare le funzioni di accesso ai file come open, stat e readlink, in modo che i percorsi che iniziano con /system venissero reindirizzati ai relativi percorsi in /apex. Il team Android ha ignorato questa opzione perché non è possibile modificare tutte le funzioni che accettano percorsi. Ad esempio, alcune app eseguono il collegamento statico di Bionic, che implementa le funzioni. In questi casi, le app non vengono reindirizzate.