Aggiornamenti di sistema dinamici

Dynamic System Updates (DSU) ti consente di creare un'immagine del sistema Android che gli utenti possono scaricare da Internet e provare senza il rischio di corrompere l'immagine del sistema corrente. Questo documento descrive come supportare DSU.

Requisiti del kernel

Vedi Implementazione di partizioni dinamiche per i requisiti del kernel.

Inoltre, DSU si affida alla funzionalità del kernel device-mapper-verity (dm-verity) per verificare l'immagine del sistema Android. Quindi devi abilitare le seguenti configurazioni del kernel:

  • CONFIG_DM_VERITY=y
  • CONFIG_DM_VERITY_FEC=y

Requisiti di partizione

A partire da Android 11, DSU richiede la partizione /data per utilizzare il file system F2FS o ext4. F2FS offre prestazioni migliori ed è consigliato, ma la differenza dovrebbe essere insignificante.

Ecco alcuni esempi della durata di un aggiornamento dinamico del sistema con un dispositivo Pixel:

  • Utilizzando F2FS:
    • 109s, utente 8G, sistema 867M, tipo di file system: F2FS: crittografia=aes-256-xts:aes-256-cts
    • 104s, utente 8G, sistema 867M, tipo di file system: F2FS: crittografia=ice
  • Utilizzando ext4:
    • 135s, utente 8G, sistema 867M, tipo di file system: ext4: crittografia=aes-256-xts:aes-256-cts

Se impiega molto più tempo sulla tua piattaforma, potresti voler controllare se il flag di montaggio contiene qualche flag che fa scrivere "sync", oppure puoi specificare esplicitamente un flag "async" per ottenere prestazioni migliori.

La partizione metadata (16 MB o superiore) è necessaria per archiviare i dati relativi alle immagini installate. Deve essere montato durante il montaggio del primo stadio.

La partizione userdata deve utilizzare il file system F2FS o ext4. Quando si utilizza F2FS, includere tutte le patch relative a F2FS disponibili nel kernel comune di Android .

DSU è stato sviluppato e testato con kernel/common 4.9. Si consiglia di utilizzare il kernel 4.9 e versioni successive per questa funzionalità.

Comportamento dell'HAL del fornitore

Tessitore HAL

L'HAL weaver fornisce un numero fisso di slot per la memorizzazione delle chiavi utente. La DSU occupa due slot chiave aggiuntivi. Se un OEM dispone di un HAL weaver, è necessario che disponga di slot sufficienti per un'immagine di sistema generica (GSI) e un'immagine host.

Portiere HAL

L' HAL Gatekeeper deve supportare valori USER_ID di grandi dimensioni, poiché il GSI compensa gli UID nell'HAL di +1.000.000.

Verifica l'avvio

Se desideri supportare l'avvio delle immagini GSI per sviluppatori nello stato BLOCCATO senza disabilitare l'avvio verificato, includi le chiavi GSI per sviluppatori aggiungendo la seguente riga al file device/<device_name>/device.mk :

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

Protezione antiritorno

Quando si utilizza DSU, l'immagine del sistema Android scaricata deve essere più recente dell'immagine del sistema corrente sul dispositivo. Questa operazione viene eseguita confrontando i livelli delle patch di sicurezza nel descrittore della proprietà AVB Android Verified Boot (AVB) di entrambe le immagini di sistema: Prop: com.android.build.system.security_patch -> '2019-04-05' .

Per i dispositivi che non utilizzano AVB, inserisci il livello della patch di sicurezza dell'immagine del sistema corrente nella riga cmdline del kernel o nel bootconfig con il bootloader: androidboot.system.security_patch=2019-04-05 .

Requisiti hardware

Quando avvii un'istanza DSU, vengono allocati due file temporanei:

  • Una partizione logica per archiviare GSI.img (1~1,5 G)
  • Una partizione /data vuota da 8 GB come sandbox per l'esecuzione di GSI

Ti consigliamo di riservare almeno 10 GB di spazio libero prima di avviare un'istanza DSU. DSU supporta anche l'allocazione da una scheda SD. Quando è presente una scheda SD, ha la massima priorità per l'allocazione. Il supporto della scheda SD è fondamentale per i dispositivi a bassa potenza che potrebbero non disporre di memoria interna sufficiente. Quando è presente una scheda SD, assicurati che non sia adottata. DSU non supporta le schede SD adottate .

Frontend disponibili

Puoi avviare DSU utilizzando adb , un'app OEM o il caricatore DSU con un clic (in Android 11 o versioni successive).

Avvia DSU utilizzando adb

Per avviare DSU utilizzando adb, inserisci questi comandi:

$ simg2img out/target/product/.../system.img system.raw
$ gzip -c system.raw > system.raw.gz
$ adb push system.raw.gz /storage/emulated/0/Download
$ adb shell am start-activity \
-n com.android.dynsystem/com.android.dynsystem.VerificationActivity  \
-a android.os.image.action.START_INSTALL    \
-d file:///storage/emulated/0/Download/system.raw.gz  \
--el KEY_SYSTEM_SIZE $(du -b system.raw|cut -f1)  \
--el KEY_USERDATA_SIZE 8589934592

Avvia DSU utilizzando un'app

Il punto di ingresso principale a DSU è l'API android.os.image.DynamicSystemClient.java :

public class DynamicSystemClient {


...
...

     /**
     * Start installing DynamicSystem from URL with default userdata size.
     *
     * @param systemUrl A network URL or a file URL to system image.
     * @param systemSize size of system image.
     */
    public void start(String systemUrl, long systemSize) {
        start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
    }

È necessario includere/preinstallare questa app sul dispositivo. Poiché DynamicSystemClient è un'API di sistema, non puoi creare l'app con la normale API SDK e non puoi pubblicarla su Google Play. Lo scopo di questa app è:

  1. Recupera un elenco di immagini e l'URL corrispondente con uno schema definito dal fornitore.
  2. Abbina le immagini nell'elenco al dispositivo e mostra immagini compatibili che l'utente può selezionare.
  3. Richiama DynamicSystemClient.start in questo modo:

    DynamicSystemClient aot = new DynamicSystemClient(...)
       aot.start(
            ...URL of the selected image...,
            ...uncompressed size of the selected image...);
    
    

L'URL punta a un file immagine di sistema compresso con gzip, che puoi creare con i seguenti comandi:

$ simg2img ${OUT}/system.img ${OUT}/system.raw
$ gzip ${OUT}/system.raw
$ ls ${OUT}/system.raw.gz

Il nome del file dovrebbe seguire questo formato:

<android version>.<lunch name>.<user defined title>.raw.gz

Esempi:

  • o.aosp_taimen-userdebug.2018dev.raw.gz
  • p.aosp_taimen-userdebug.2018dev.raw.gz

Caricatore DSU con un clic

Android 11 introduce il caricatore DSU con un clic, che è un frontend nelle impostazioni dello sviluppatore.

Avvio del caricatore DSU

Figura 1. Avvio del caricatore DSU

Quando lo sviluppatore fa clic sul pulsante DSU Loader , recupera un descrittore DSU JSON preconfigurato dal Web e visualizza tutte le immagini applicabili nel menu mobile. Seleziona un'immagine per avviare l'installazione della DSU e l'avanzamento verrà visualizzato sulla barra di notifica.

Avanzamento dell'installazione dell'immagine DSU

Figura 2. Avanzamento dell'installazione dell'immagine DSU

Per impostazione predefinita, il caricatore DSU carica un descrittore JSON che contiene le immagini GSI. Le sezioni seguenti illustrano come creare pacchetti DSU firmati OEM e caricarli dal caricatore DSU.

Flag di funzionalità

La funzione DSU si trova sotto il flag della funzione settings_dynamic_android . Prima di utilizzare DSU, assicurarsi che il flag di funzionalità corrispondente sia abilitato.

Abilitazione del flag di funzionalità.

Figura 3. Abilitazione del flag di funzionalità

L'interfaccia utente del flag di funzionalità potrebbe non essere disponibile su un dispositivo che esegue una build utente. In questo caso, utilizza invece il comando adb :

$ adb shell setprop persist.sys.fflag.override.settings_dynamic_system 1

Immagini del sistema host del fornitore su GCE (facoltativo)

Una delle possibili posizioni di archiviazione per le immagini di sistema è il bucket Google Compute Engine (GCE). L'amministratore della versione utilizza la console di archiviazione GCP per aggiungere/eliminare/modificare l'immagine del sistema rilasciata.

Le immagini devono essere ad accesso pubblico, come mostrato qui:

Accesso pubblico in GCE

Figura 4. Accesso pubblico nella GCE

La procedura per rendere pubblico un elemento è disponibile nella documentazione di Google Cloud .

DSU a più partizioni nel file ZIP

A partire da Android 11, DSU può avere più di una partizione. Ad esempio, può contenere product.img oltre a system.img . Quando il dispositivo si avvia, la prima fase init rileva le partizioni DSU installate e sostituisce temporaneamente la partizione sul dispositivo, quando la DSU installata è abilitata. Il pacchetto DSU potrebbe contenere una partizione a cui non è associata una partizione corrispondente nel dispositivo.

Processo DSU con più partizioni

Figura 5. Processo DSU con più partizioni

DSU firmato OEM

Per garantire che tutte le immagini in esecuzione sul dispositivo siano autorizzate dal produttore del dispositivo, tutte le immagini all'interno di un pacchetto DSU devono essere firmate. Ad esempio, supponiamo che sia presente un pacchetto DSU che contiene due immagini di partizioni come di seguito:

dsu.zip {
    - system.img
    - product.img
}

Sia system.img che product.img devono essere firmati dalla chiave OEM prima di essere inseriti nel file ZIP. La pratica comune è utilizzare un algoritmo asimmetrico, ad esempio RSA, in cui la chiave segreta viene utilizzata per firmare il pacchetto e la chiave pubblica per verificarlo. Il ramdisk della prima fase deve includere la chiave pubblica di abbinamento, ad esempio /avb/*.avbpubkey . Se il dispositivo adotta già l'AVB, sarà sufficiente la procedura di firma esistente. Le sezioni seguenti illustrano il processo di firma ed evidenziano il posizionamento della pubkey AVB utilizzata per verificare le immagini nel pacchetto DSU.

Descrittore DSU JSON

Il descrittore JSON DSU descrive i pacchetti DSU. Supporta due primitive. Innanzitutto, la primitiva include include descrittori JSON aggiuntivi o reindirizza il caricatore DSU in una nuova posizione. Per esempio:

{
    "include": ["https://.../gsi-release/gsi-src.json"]
}

In secondo luogo, la primitiva image viene utilizzata per descrivere i pacchetti DSU rilasciati. All'interno dell'immagine primitiva sono presenti diversi attributi:

  • Gli attributi name e details sono stringhe visualizzate nella finestra di dialogo affinché l'utente possa selezionarle.

  • Gli attributi cpu_api , vndk e os_version vengono utilizzati per i controlli di compatibilità, descritti nella sezione successiva.

  • L'attributo facoltativo pubkey descrive la chiave pubblica che si accoppia con la chiave segreta utilizzata per firmare il pacchetto DSU. Quando viene specificato, il servizio DSU può verificare se il dispositivo dispone della chiave utilizzata per verificare il pacchetto DSU. Ciò evita l'installazione di un pacchetto DSU non riconosciuto, ad esempio l'installazione di una DSU firmata da OEM-A su un dispositivo prodotto da OEM-B.

  • L'attributo facoltativo tos punta a un file di testo che descrive i termini di servizio per il pacchetto DSU corrispondente. Quando uno sviluppatore seleziona un pacchetto DSU con i termini di servizio specificati, si apre la finestra di dialogo mostrata nella Figura 6, che chiede allo sviluppatore di accettare i termini di servizio prima di installare il pacchetto DSU.

    Finestra di dialogo Termini di servizio

    Figura 6. Finestra di dialogo Termini di servizio

Per riferimento, ecco un descrittore JSON DSU per GSI:

{
   "images":[
      {
         "name":"GSI+GMS x86",
         "os_version":"10",
         "cpu_abi": "x86",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
         "uri":"https://.../gsi/gsi_gms_x86-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI+GMS ARM64",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
         "uri":"https://.../gsi/gsi_gms_arm64-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI ARM64",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "uri":"https://.../gsi/aosp_arm64-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI x86_64",
         "os_version":"10",
         "cpu_abi": "x86_64",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "uri":"https://.../gsi/aosp_x86_64-exp-QP1A.190711.020.C4-5928301.zip"
      }
   ]
}

Gestione della compatibilità

Vengono utilizzati diversi attributi per specificare la compatibilità tra un pacchetto DSU e il dispositivo locale:

  • cpu_api è una stringa che descrive l'architettura del dispositivo. Questo attributo è obbligatorio e viene confrontato con la proprietà di sistema ro.product.cpu.abi . I loro valori devono corrispondere esattamente.

  • os_version è un numero intero facoltativo che specifica una versione Android. Ad esempio, per Android 10, os_version è 10 e per Android 11, os_version è 11 . Quando questo attributo viene specificato, deve essere uguale o maggiore della proprietà di sistema ro.system.build.version.release . Questo controllo viene utilizzato per impedire l'avvio di un'immagine GSI Android 10 su un dispositivo del fornitore Android 11, che attualmente non è supportato. È consentito l'avvio di un'immagine GSI Android 11 su un dispositivo Android 10.

  • vndk è un array facoltativo che specifica tutti i VNDK inclusi nel pacchetto DSU. Quando viene specificato, il caricatore DSU controlla se il numero estratto dalla proprietà di sistema ro.vndk.version è incluso.

Revoca le chiavi DSU per motivi di sicurezza

Nel caso estremamente raro in cui la coppia di chiavi RSA utilizzata per firmare le immagini DSU viene compromessa, il ramdisk deve essere aggiornato il prima possibile per rimuovere la chiave compromessa. Oltre ad aggiornare la partizione di avvio, puoi bloccare le chiavi compromesse utilizzando un elenco di revoche di chiavi DSU (lista nera di chiavi) da un URL HTTPS.

L'elenco di revoche di chiavi DSU contiene un elenco di chiavi pubbliche AVB revocate. Durante l'installazione della DSU, le chiavi pubbliche all'interno delle immagini DSU vengono convalidate con l'elenco di revoche. Se viene rilevato che le immagini contengono una chiave pubblica revocata, il processo di installazione della DSU si interrompe.

L'URL dell'elenco di revoche di chiavi deve essere un URL HTTPS per garantire il livello di sicurezza ed è specificato in una stringa di risorsa:

frameworks/base/packages/DynamicSystemInstallationService/res/values/strings.xml@key_revocation_list_url

Il valore della stringa è https://dl.google.com/developers/android/gsi/gsi-keyblacklist.json , che è un elenco di revoche per le chiavi GSI rilasciate da Google. Questa stringa di risorse può essere sovrapposta e personalizzata, in modo che gli OEM che adottano la funzionalità DSU possano fornire e mantenere la propria lista nera delle chiavi. Ciò fornisce all'OEM un modo per bloccare determinate chiavi pubbliche senza aggiornare l'immagine del ramdisk del dispositivo.

Il formato dell'elenco di revoca è:

{
   "entries":[
      {
         "public_key":"bf14e439d1acf231095c4109f94f00fc473148e6",
         "status":"REVOKED",
         "reason":"Key revocation test key"
      },
      {
         "public_key":"d199b2f29f3dc224cca778a7544ea89470cbef46",
         "status":"REVOKED",
         "reason":"Key revocation test key"
      }
   ]
}
  • public_key è il digest SHA-1 della chiave revocata, nel formato descritto nella sezione relativa alla generazione della pubkey AVB .
  • status indica lo stato di revoca della chiave. Attualmente, l'unico valore supportato è REVOKED .
  • reason è una stringa facoltativa che descrive il motivo della revoca.

Procedure DSU

Questa sezione descrive come eseguire diverse procedure di configurazione della DSU.

Genera una nuova coppia di chiavi

Utilizza il comando openssl per generare una coppia di chiavi RSA privata/pubblica in formato .pem (ad esempio, con dimensione 2048 bit):

$ openssl genrsa -out oem_cert_pri.pem 2048
$ openssl rsa -in oem_cert_pri.pem -pubout -out oem_cert_pub.pem

La chiave privata potrebbe non essere accessibile ed è conservata solo in un modulo di sicurezza hardware (HSM) . In questo caso, dopo la generazione della chiave potrebbe essere disponibile un certificato di chiave pubblica x509. Consulta la sezione Aggiunta della pubkey di abbinamento al ramdisk per istruzioni su come generare la chiave pubblica AVB da un certificato x509.

Per convertire un certificato x509 in formato PEM:

$ openssl x509 -pubkey -noout -in oem_cert_pub.x509.pem > oem_cert_pub.pem

Salta questo passaggio se il certificato è già un file PEM.

Aggiungi la pubkey di accoppiamento al ramdisk

oem_cert.avbpubkey deve essere inserito in /avb/*.avbpubkey per verificare il pacchetto DSU firmato. Innanzitutto, converti la chiave pubblica in formato PEM nel formato chiave pubblica AVB:

$ avbtool extract_public_key --key oem_cert_pub.pem --output oem_cert.avbpubkey

Quindi includere la chiave pubblica nel ramdisk della prima fase con i seguenti passaggi.

  1. Aggiungi un modulo predefinito per copiare avbpubkey . Ad esempio, aggiungi device/<company>/<board>/oem_cert.avbpubkey e device/<company>/<board>/avb/Android.mk con contenuti come questi:

    include $(CLEAR_VARS)
    
    LOCAL_MODULE := oem_cert.avbpubkey
    LOCAL_MODULE_CLASS := ETC
    LOCAL_SRC_FILES := $(LOCAL_MODULE)
    ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
    LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
    else
    LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
    endif
    
    include $(BUILD_PREBUILT)
    
  2. Fai in modo che il target droidcore dipenda oem_cert.avbpubkey aggiunto:

    droidcore: oem_cert.avbpubkey
    

Genera l'attributo pubkey AVB nel descrittore JSON

oem_cert.avbpubkey è nel formato binario a chiave pubblica AVB. Utilizza SHA-1 per renderlo leggibile prima di inserirlo nel descrittore JSON:

$ sha1sum oem_cert.avbpubkey | cut -f1 -d ' '
3e62f2be9d9d813ef5........866ac72a51fd20

Questo sarà il contenuto dell'attributo pubkey del descrittore JSON.

   "images":[
      {
         ...
         "pubkey":"3e62f2be9d9d813ef5........866ac72a51fd20",
         ...
      },

Firma un pacchetto DSU

Utilizza uno di questi metodi per firmare un pacchetto DSU:

  • Metodo 1: riutilizzare l'artefatto creato dal processo di firma AVB originale per creare un pacchetto DSU. Un approccio alternativo consiste nell'estrarre le immagini già firmate dal pacchetto di rilascio e utilizzare le immagini estratte per creare direttamente il file ZIP.

  • Metodo 2: utilizzare i seguenti comandi per firmare le partizioni DSU se la chiave privata è disponibile. Ogni img all'interno di un pacchetto DSU (il file ZIP) è firmato separatamente:

    $ key_len=$(openssl rsa -in oem_cert_pri.pem -text | grep Private-Key | sed -e 's/.*(\(.*\) bit.*/\1/')
    $ for partition in system product; do
        avbtool add_hashtree_footer \
            --image ${OUT}/${partition}.img \
            --partition_name ${partition} \
            --algorithm SHA256_RSA${key_len} \
            --key oem_cert_pri.pem
    done
    

Per ulteriori informazioni sull'aggiunta di add_hashtree_footer utilizzando avbtool , vedere Utilizzo di avbtool .

Verificare il pacchetto DSU localmente

Si consiglia di verificare tutte le immagini locali rispetto alla chiave pubblica di abbinamento con questi comandi:


for partition in system product; do
    avbtool verify_image --image ${OUT}/${partition}.img  --key oem_cert_pub.pem
done

L'output previsto è simile al seguente:

Verifying image dsu/system.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/system.img
: Successfully verified sha1 hashtree of dsu/system.img for image of 898494464 bytes

Verifying image dsu/product.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/product.img
: Successfully verified sha1 hashtree of dsu/product.img for image of 905830400 bytes

Crea un pacchetto DSU

L'esempio seguente crea un pacchetto DSU che contiene system.img e product.img :

dsu.zip {
    - system.img
    - product.img
}

Dopo aver firmato entrambe le immagini, utilizzare il comando seguente per creare il file ZIP:

$ mkdir -p dsu
$ cp ${OUT}/system.img dsu
$ cp ${OUT}/product.img dsu
$ cd dsu && zip ../dsu.zip *.img && cd -

Personalizza la DSU con un clic

Per impostazione predefinita, il caricatore DSU punta a metadati di immagini GSI che sono https://...google.com/.../gsi-src.json .

Gli OEM possono sovrascrivere l'elenco definendo la proprietà persist.sys.fflag.override.settings_dynamic_system.list che punta al proprio descrittore JSON. Ad esempio, un OEM può fornire metadati JSON che includono GSI e immagini proprietarie OEM come queste:

{
    "include": ["https://dl.google.com/.../gsi-src.JSON"]
    "images":[
      {
         "name":"OEM image",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"...",
         "vndk":[
            27,
            28,
            29
         ],
         "spl":"...",
         "pubkey":"",
         "uri":"https://.../....zip"
      },

}

È possibile per un OEM concatenare i metadati DSU pubblicati come illustrato nella Figura 7.

Concatenamento dei metadati DSU pubblicati

Figura 7. Concatenamento dei metadati DSU pubblicati