Fornitore APEX

Puoi utilizzare il formato file APEX per pacchettizzare e installare moduli del sistema operativo Android di livello inferiore. Consente la compilazione e l'installazione indipendenti di componenti come servizi e librerie native, implementazioni HAL, firmware, file di configurazione e così via.

I componenti APEX del fornitore vengono installati automaticamente dal sistema di compilazione nella partizione /vendor e attivati in fase di esecuzione da apexd, proprio come i componenti APEX in altre partizioni.

Casi d'uso

Modularizzazione delle immagini dei fornitori

Gli APEX facilitano il naturale raggruppamento e la modularizzazione delle implementazioni delle funzionalità sulle immagini dei fornitori.

Quando le immagini dei fornitori vengono create come combinazione di APEX dei fornitori creati in modo indipendente, i produttori di dispositivi possono scegliere facilmente le implementazioni specifiche dei fornitori sul loro dispositivo. I produttori possono anche creare un nuovo APEX del fornitore se nessuno di quelli forniti soddisfa le loro esigenze o se dispongono di un hardware personalizzato nuovo di zecca.

Ad esempio, un OEM potrebbe scegliere di comporre il proprio dispositivo con l'APEX di implementazione del Wi-Fi AOSP, l'APEX di implementazione del Bluetooth SoC e un APEX di implementazione della telefonia OEM personalizzato.

Senza APEX del fornitore, un'implementazione con così tante dipendenze tra i componenti del fornitore richiede un attento coordinamento e monitoraggio. Se inserisci tutti i componenti (inclusi i file di configurazione e le librerie aggiuntive) in APEX con interfacce chiaramente definite in qualsiasi punto di comunicazione tra funzionalità, i diversi componenti diventano intercambiabili.

Iterazione dello sviluppatore

Gli APEX del fornitore aiutano gli sviluppatori a eseguire l'iterazione più velocemente durante lo sviluppo dei moduli del fornitore raggruppando un'intera implementazione della funzionalità, come l'HAL Wi-Fi, all'interno di un APEX del fornitore. Gli sviluppatori possono quindi compilare e inviare singolarmente l'APEX del fornitore per testare le modifiche, anziché ricostruire l'intera immagine del fornitore.

Questo semplifica e accelera il ciclo di iterazione degli sviluppatori che lavorano principalmente in un'area delle funzionalità e vogliono eseguire l'iterazione solo su quell'area.

Il raggruppamento naturale di un'area di funzionalità in un'espressione APEX semplifica anche la procedura di compilazione, invio e test delle modifiche per l'area di funzionalità. Ad esempio, la reinstallazione di un APEX aggiorna automaticamente tutte le librerie o i file di configurazione in bundle inclusi nell'APEX.

Il raggruppamento di un'area di funzionalità in un APEX semplifica anche il debug o il ripristino quando viene osservato un comportamento anomalo del dispositivo. Ad esempio, se la telefonia non funziona correttamente in una nuova build, gli sviluppatori potrebbero provare a installare un APEX di implementazione della telefonia precedente su un dispositivo (senza dover eseguire il flashing di una build completa) e verificare se il comportamento corretto viene ripristinato.

Flusso di lavoro di esempio:

# Build the entire device and flash. OR, obtain an already-flashed device.
source build/envsetup.sh && lunch oem_device-userdebug
m
fastboot flashall -w

# Test the device.
... testing ...

# Check previous behavior using a vendor APEX from one week ago, downloaded from
# your continuous integration build.
... download command ...
adb install <path to downloaded APEX>
adb reboot
... testing ...

# Edit and rebuild just the APEX to change and test behavior.
... edit APEX source contents ...
m <apex module name>
adb install out/<path to built APEX>
adb reboot
... testing ...

Esempi

Nozioni di base

Per informazioni generiche su APEX, consulta la pagina principale Formato file APEX, inclusi i requisiti del dispositivo, i dettagli sul formato file e i passaggi di installazione.

In Android.bp, l'impostazione della proprietà vendor: true rende un modulo APEX un APEX del fornitore.

apex {
  ..
  vendor: true,
  ..
}

Binari e librerie condivise

Un APEX include dipendenze trasitive all'interno del payload APEX, a meno che non abbiano interfacce stabili.

Le interfacce native stabili per le dipendenze APEX del fornitore includono cc_library con stubs e le librerie LLNDK. Queste dipendenze vengono escluse dal packaging e registrate nel file manifest APEX. Il manifest viene elaborato da linkerconfig in modo che le dipendenze native esterne siano disponibili in fase di esecuzione.

Nel seguente snippet, APEX contiene sia il file binario (my_service) sia le sue dipendenze non stabili (file *.so).

apex {
  ..
  vendor: true,
  binaries: ["my_service"],
  ..
}

Nel seguente snippet, il file APEX contiene la libreria condivisamy_standalone_lib e le eventuali dipendenze non stabili (come descritto sopra).

apex {
  ..
  vendor: true,
  native_shared_libs: ["my_standalone_lib"],
  ..
}

Riduci le dimensioni di APEX

APEX potrebbe aumentare di dimensioni perché raggruppa dipendenze non stabili. Ti consigliamo di utilizzare il collegamento statico. Le librerie comuni come libc++.so e libbase.so possono essere collegate in modo statico ai file binari HAL. Un'altra opzione è creare una dipendenza per fornire un'interfaccia stabile. La dipendenza non verrà inclusa in APEX.

Implementazioni HAL

Per definire un'implementazione HAL, fornisci i binari e le librerie corrispondenti all'interno di un APEX del fornitore simile ai seguenti esempi:

Per incapsulare completamente l'implementazione dell'HAL, l'APEX deve specificare anche eventuali frammenti VINTF e script di inizializzazione pertinenti.

Frammenti VINTF

I frammenti VINTF possono essere pubblicati da un APEX del fornitore quando si trovano in etc/vintf dell'APEX.

Utilizza la proprietà prebuilts per incorporare i frammenti VINTF in APEX.

apex {
  ..
  vendor: true,
  prebuilts: ["fragment.xml"],
  ..
}

prebuilt_etc {
  name: "fragment.xml",
  src: "fragment.xml",
  sub_dir: "vintf",
}

API di query

Quando i frammenti VINTF vengono aggiunti ad APEX, utilizza le API libbinder_ndk per ottenere le mappature delle interfacce HAL e dei nomi APEX.

  • AServiceManager_isUpdatableViaApex("com.android.foo.IFoo/default") : true se l'istanza HAL è definita in APEX.
  • AServiceManager_getUpdatableApexName("com.android.foo.IFoo/default", ...) : restituisce il nome APEX che definisce l'istanza HAL.
  • AServiceManager_openDeclaredPassthroughHal("mapper", "instance", ...): utilizza questa opzione per aprire un HAL passthrough.

Script di inizializzazione

Gli APEX possono includere script di inizializzazione in due modi: (A) un file di testo precompilato all'interno del payload APEX o (B) un normale script di inizializzazione in /vendor/etc. Puoi impostare entrambi per lo stesso APEX.

Script di inizializzazione in APEX:

prebuilt_etc {
  name: "myinit.rc",
  src: "myinit.rc"
}

apex {
  ..
  vendor: true,
  prebuilts: ["myinit.rc"],
  ..
}

Gli script di inizializzazione negli APEX del fornitore possono avere definizioni service e on <property or event> istruzioni.

Assicurati che una definizione service indichi un file binario nello stesso APEX. Ad esempio, com.android.foo APEX potrebbe definire un servizio denominato foo-service.

on foo-service /apex/com.android.foo/bin/foo
  ...

Fai attenzione quando utilizzi le direttive on. Poiché gli script di inizializzazione negli APEX vengono analizzati ed eseguiti dopo l'attivazione degli APEX, non è possibile utilizzare alcuni eventi o proprietà. Utilizza apex.all.ready=true per attivare le azioni il prima possibile. Gli APEX di bootstrap possono utilizzare on init, ma non on early-init.

Firmware

Esempio:

Incorpora il firmware in un APEX del fornitore con il tipo di modulo prebuilt_firmware, come segue.

prebuilt_firmware {
  name: "my.bin",
  src: "path_to_prebuilt_firmware",
  vendor: true,
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.bin"],  // installed inside APEX as /etc/firmware/my.bin
  ..
}

I moduli prebuilt_firmware vengono installati nella directory <apex name>/etc/firmware dell'APEX. ueventd esegue la scansione delle directory /apex/*/etc/firmware per trovare i moduli del firmware.

Il file_contexts dell'APEX deve etichettare correttamente le eventuali voci del payload del firmware per garantire che questi file siano accessibili da ueventd in fase di esecuzione. In genere, l'etichetta vendor_file è sufficiente. Ad esempio:

(/.*)? u:object_r:vendor_file:s0

Moduli del kernel

Incorpora i moduli kernel in un APEX del fornitore come moduli predefiniti, come segue.

prebuilt_etc {
  name: "my.ko",
  src: "my.ko",
  vendor: true,
  sub_dir: "modules"
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.ko"],  // installed inside APEX as /etc/modules/my.ko
  ..
}

Il file_contexts dell'APEX deve etichettare correttamente le voci del payload del modulo del kernel. Ad esempio:

/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0

I moduli del kernel devono essere installati in modo esplicito. Il seguente script di init di esempio nella partizione del fornitore mostra l'installazione tramite insmod:

my_init.rc:

on early-boot
  insmod /apex/myapex/etc/modules/my.ko
  ..

Overlay delle risorse di runtime

Esempio:

Incorpora overlay di risorse di runtime nell'APEX di un fornitore utilizzando la proprietà rros.

runtime_resource_overlay {
    name: "my_rro",
    soc_specific: true,
}


apex {
  ..
  vendor: true,
  rros: ["my_rro"],  // installed inside APEX as /overlay/my_rro.apk
  ..
}

Altri file di configurazione

Gli APEX del fornitore supportano vari altri file di configurazione che in genere si trovano nella partizione del fornitore come predefiniti all'interno degli APEX del fornitore e ne vengono aggiunti altri.

Esempi:

APEX del fornitore di bootstrap

Alcuni servizi HAL come keymint dovrebbero essere disponibili prima dell'attivazione degli APEX. In genere questi HAL impostano early_hal nella definizione del servizio nello script init. Un altro esempio è la classe animation, che in genere viene avviata prima dell'evento post-fs-data. Quando un servizio HAL di questo tipo viene pacchettizzato nell'APEX del fornitore, imposta l'apice "vendorBootstrap": true nel file manifest APEX in modo che possa essere attivato prima. Tieni presente che gli APEX di bootstrap possono essere attivati solo dalla posizione predefinita, ad esempio /vendor/apex, non da /data/apex.

Proprietà di sistema

Queste sono le proprietà di sistema che il framework legge per supportare gli APEX dei fornitori:

  • input_device.config_file.apex=<apex name>: se impostato, i file di configurazione di input (*.idc, *.kl e *.kcm) vengono cercati nella directory /etc/usr di APEX.
  • ro.vulkan.apex=<apex name>: se impostato, il driver Vulkan viene caricato dall'APEX. Poiché il driver Vulkan viene utilizzato dalle prime HAL, imposta APEX su Bootstrap APEX e configura l'esplorazione dello spazio dei nomi del linker.

Imposta le proprietà di sistema negli script di inizializzazione utilizzando il comando setprop.

Funzionalità di sviluppo aggiuntive

Selezione di APEX all'avvio

Esempio:

Gli sviluppatori possono anche installare più versioni degli APEX del fornitore che condividono lo stesso nome e la stessa chiave APEX, quindi scegliere la versione da attivare a ogni avvio utilizzando sysprop permanenti. Per determinati casi d'uso degli sviluppatori, questa operazione potrebbe essere più semplice rispetto all'installazione di una nuova copia di APEX utilizzando adb install.

Esempi di casi d'uso:

  • Installa 3 versioni dell'APEX del fornitore HAL Wi-Fi: i team QA possono eseguire test manuali o automatici utilizzando una versione, quindi riavviare in un'altra versione e eseguire nuovamente i test, per poi confrontare i risultati finali.
  • Installa due versioni del fornitore HAL della fotocamera APEX, attuale e sperimentale: i dogfooder possono utilizzare la versione sperimentale senza scaricare e installare un file aggiuntivo, quindi possono tornare facilmente alla versione precedente.

Durante l'avvio, apexd cerca sysprops in un formato specifico per attivare la versione APEX corretta.

I formati previsti per la chiave della proprietà sono:

  • Configurazione di avvio
    • Utilizzato per impostare il valore predefinito in BoardConfig.mk.
    • androidboot.vendor.apex.<apex name>
  • Proprietà sysprop permanente
    • Utilizzato per modificare il valore predefinito impostato su un dispositivo già avviato.
    • Sostituisce il valore bootconfig, se presente.
    • persist.vendor.apex.<apex name>

Il valore della proprietà deve essere il nome file dell'APEX da attivare.

// Default version.
apex {
  name: "com.oem.camera.hal.my_apex_default",
  vendor: true,
  ..
}

// Non-default version.
apex {
  name: "com.oem.camera.hal.my_apex_experimental",
  vendor: true,
  ..
}

La versione predefinita deve essere configurata anche utilizzando bootconfig in BoardConfig.mk:

# Example for APEX "com.oem.camera.hal" with the default above:
BOARD_BOOTCONFIG += \
    androidboot.vendor.apex.com.oem.camera.hal=com.oem.camera.hal.my_apex_default

Dopo l'avvio del dispositivo, modifica la versione attivata impostando la proprietà sysprop persistente:

$ adb root;
$ adb shell setprop \
    persist.vendor.apex.com.oem.camera.hal \
    com.oem.camera.hal.my_apex_experimental;
$ adb reboot;

Se il dispositivo supporta l'aggiornamento di bootconfig dopo il flashing (ad esempio tramite i comandi fastboot oem), la modifica della proprietà bootconfig per APEX installato su più dispositivi modifica anche la versione attivata all'avvio.

Per i dispositivi di riferimento virtuali basati su Cuttlefish, puoi utilizzare il comando --extra_bootconfig_args per impostare la proprietà bootconfig direttamente durante l'avvio. Ad esempio:

launch_cvd --noresume \
  --extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";