Modifica il valore delle risorse di un'app in fase di esecuzione

Un runtime Resource Overlay (RRO) è un pacchetto che modifica i valori delle risorse di un pacchetto di destinazione in fase di runtime. Ad esempio, un'app installata sull'immagine di sistema potrebbe modificare il proprio comportamento in base al valore di una risorsa. Invece di codificare il valore della risorsa in fase di compilazione, un RRO installato su una partizione diversa può modificare i valori delle risorse dell'app in fase di esecuzione.

Le RRO possono essere abilitate o disabilitate. È possibile impostare a livello di codice lo stato di abilitazione/disabilitazione per attivare/disattivare la capacità di un RRO di modificare i valori delle risorse. Le RRO sono disabilitate per impostazione predefinita (tuttavia, le RRO statiche sono abilitate per impostazione predefinita).

Risorse sovrapposte

Le sovrapposizioni funzionano mappando le risorse definite nel pacchetto overlay sulle risorse definite nel pacchetto di destinazione. Quando un'app tenta di risolvere il valore di una risorsa nel pacchetto di destinazione, viene invece restituito il valore della risorsa sovrapposta a cui è mappata la risorsa di destinazione.

Imposta il manifesto

Un pacchetto è considerato un pacchetto RRO se contiene un tag <overlay> come figlio del tag <manifest> .

  • Il valore dell'attributo richiesto android:targetPackage specifica il nome del pacchetto che RRO intende sovrapporre.

  • Il valore dell'attributo facoltativo android:targetName specifica il nome del sottoinsieme sovrapponibile di risorse del pacchetto di destinazione che RRO intende sovrapporre. Se la destinazione non definisce un insieme di risorse sovrapponibili, questo attributo non dovrebbe essere presente.

Il codice seguente mostra un esempio di overlay AndroidManifest.xml .

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.overlay">
    <application android:hasCode="false" />
    <overlay android:targetPackage="com.example.target"
                   android:targetName="OverlayableResources"/>
</manifest>

Gli overlay non possono sovrapporre il codice, quindi non possono avere file DEX. Inoltre, l'attributo android:hasCode del tag <application > nel manifest deve essere impostato su false .

Definire la mappa delle risorse

In Android 11 o versioni successive, il meccanismo consigliato per definire la mappa delle risorse di sovrapposizione è creare un file nella directory res/xml del pacchetto di sovrapposizione, enumerare le risorse di destinazione che dovrebbero essere sovrapposte e i relativi valori di sostituzione, quindi impostare il valore di attributo android:resourcesMap del tag manifest <overlay> in un riferimento al file di mappatura delle risorse.

Il codice seguente mostra un file res/xml/overlays.xml di esempio.

<?xml version="1.0" encoding="utf-8"?>
<overlay xmlns:android="http://schemas.android.com/apk/res/android" >
    <!-- Overlays string/config1 and string/config2 with the same resource. -->
    <item target="string/config1" value="@string/overlay1" />
    <item target="string/config2" value="@string/overlay1" />

    <!-- Overlays string/config3 with the string "yes". -->
    <item target="string/config3" value="@android:string/yes" />

    <!-- Overlays string/config4 with the string "Hardcoded string". -->
    <item target="string/config4" value="Hardcoded string" />

    <!-- Overlays integer/config5 with the integer "42". -->
    <item target="integer/config5" value="42" />
</overlay>

Il codice seguente mostra un manifesto di sovrapposizione di esempio.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.overlay">
    <application android:hasCode="false" />
    <overlay android:targetPackage="com.example.target"
                   android:targetName="OverlayableResources"
                   android:resourcesMap="@xml/overlays"/>
</manifest>

Costruisci il pacchetto

Android 11 o versioni successive supporta una regola di build Soong per gli overlay che impedisce ad Android Asset Packaging Tool 2 (AAPT2) di tentare di deduplicare configurazioni di risorse con lo stesso valore ( --no-resource-deduping ) e di rimuovere risorse senza configurazioni predefinite ( --no-resource-removal ). Il codice seguente mostra un file Android.bp di esempio.

runtime_resource_overlay {
    name: "ExampleOverlay",
    sdk_version: "current",
}

Risolvi le risorse

Se una risorsa di destinazione o una risorsa sovrapposta ha più configurazioni definite per la risorsa da interrogare, il runtime delle risorse restituisce il valore della configurazione che meglio corrisponde alla configurazione del dispositivo. Per determinare quale configurazione è la configurazione con la migliore corrispondenza, unisci l'insieme delle configurazioni delle risorse sovrapposte nell'insieme delle configurazioni delle risorse di destinazione e quindi segui il normale flusso di risoluzione delle risorse (per i dettagli, fai riferimento a Come Android trova la risorsa con la migliore corrispondenza ).

Ad esempio, se un overlay definisce un valore per la configurazione drawable-en e la destinazione definisce un valore per drawable-en-port , drawable-en-port ha una corrispondenza migliore, quindi il valore della configurazione di destinazione drawable-en-port è scelto in fase di esecuzione. Per sovrapporre tutte le configurazioni drawable-en , la sovrapposizione deve definire un valore per ciascuna configurazione drawable-en definita dalla destinazione.

Gli overlay possono fare riferimento alle proprie risorse, con comportamenti diversi tra le versioni Android.

  • In Android 11 o versioni successive, ogni overlay ha il proprio spazio ID risorsa riservato che non si sovrappone allo spazio ID risorsa di destinazione o ad altri spazi ID risorsa overlay, quindi gli overlay che fanno riferimento alle proprie risorse funzionano come previsto.

  • In Android 10 o versioni precedenti, gli overlay e i pacchetti di destinazione condividono lo stesso spazio ID risorsa, il che può causare collisioni e comportamenti imprevisti quando tentano di fare riferimento alle proprie risorse utilizzando la sintassi @type/name .

Abilita/disabilita gli overlay

Utilizza l'API OverlayManager per abilitare e disabilitare gli overlay modificabili (recupera l'interfaccia API utilizzando Context#getSystemService(Context.OVERLAY_SERVICE) ). Un overlay può essere abilitato solo dal pacchetto a cui è destinato o da un pacchetto con l'autorizzazione android.permission.CHANGE_OVERLAY_PACKAGES . Quando un overlay viene abilitato o disabilitato, gli eventi di modifica della configurazione si propagano al pacchetto di destinazione e le attività di destinazione vengono riavviate.

Limita le risorse sovrapponibili

In Android 10 o versioni successive, il tag XML <overlayable> espone un set di risorse che gli RRO possono sovrapporre. Nel seguente esempio il file res/values/overlayable.xml , string/foo e integer/bar sono risorse utilizzate per personalizzare l'aspetto del dispositivo; per sovrapporre queste risorse, una sovrapposizione deve indirizzare esplicitamente la raccolta di risorse sovrapponibili in base al nome.

<!-- The collection of resources for theming the appearance of the device -->
<overlayable name="ThemeResources">
       <policy type="public">
               <item type="string" name="foo/" />
               <item type="integer" name="bar/" />
       </policy>
       ...
</overlayable>

Un APK può definire più tag <overlayable> , ma ogni tag deve avere un nome univoco all'interno del pacchetto. Ad esempio, è:

  • OK per due pacchetti diversi per definire entrambi <overlayable name="foo"> .

  • Non è consentito che un singolo APK abbia due blocchi <overlayable name="foo"> .

Il codice seguente mostra un esempio di sovrapposizione nel file AndroidManifest.xml .

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.my.theme.overlay">
       <application android:hasCode="false" />
       <!-- This overlay will override the ThemeResources resources -->
       <overlay android:targetPackage="android" android:targetName="ThemeResources">
</manifest>

Quando un'app definisce un tag <overlayable> , gli overlay hanno come target quell'app:

  • È necessario specificare targetName .

  • Può sovrapporre solo le risorse elencate nel tag <overlayable> .

  • Può scegliere come target un solo nome <overlayable> .

Non è possibile abilitare un overlay destinato a un pacchetto che espone risorse sovrapponibili ma non utilizza android:targetName per indirizzare un tag <overlayable> specifico.

Limitare le politiche

Utilizza il tag <policy> per applicare restrizioni sulle risorse sovrapponibili. L'attributo type specifica quali policy deve soddisfare un overlay per sovrascrivere le risorse incluse. I tipi supportati includono quanto segue.

  • public . Qualsiasi sovrapposizione può sovrascrivere la risorsa.
  • system . Qualsiasi sovrapposizione sulla partizione di sistema può sovrascrivere le risorse.
  • vendor . Qualsiasi sovrapposizione sulla partizione del fornitore può sovrascrivere le risorse.
  • product . Qualsiasi sovrapposizione sulla partizione del prodotto può sovrascrivere le risorse.
  • oem . Qualsiasi sovrapposizione sulla partizione oem può sovrascrivere le risorse.
  • odm . Qualsiasi sovrapposizione sulla partizione odm può sovrascrivere le risorse.
  • signature . Qualsiasi overlay firmato con la stessa firma dell'APK di destinazione può sovrascrivere le risorse.
  • actor . Qualsiasi overlay firmato con la stessa firma dell'APK dell'attore può sovrascrivere le risorse. L'attore è dichiarato nel tag denominato-attore nella configurazione di sistema.
  • config_signature . Qualsiasi overlay firmato con la stessa firma dell'apk overlay-config può sovrascrivere le risorse. Il overlay-config è dichiarato nel tag overlay-config-signature nella configurazione di sistema.

Il codice seguente mostra un esempio di tag <policy> nel file res/values/overlayable.xml .

<overlayable name="ThemeResources">
   <policy type="vendor" >
       <item type="string" name="foo" />
   </policy>
   <policy type="product|signature"  >
       <item type="string" name="bar" />
       <item type="string" name="baz" />
   </policy>
</overlayable>

Per specificare più policy, utilizzare le barre verticali (|) come caratteri separatori. Quando vengono specificate più policy, un overlay deve soddisfare solo una policy per sovrascrivere le risorse elencate nel tag <policy> .

Configura gli overlay

Android supporta diversi meccanismi per la configurazione della mutabilità, dello stato predefinito e della priorità degli overlay a seconda della versione di rilascio di Android.

  • I dispositivi che eseguono Android 11 o versioni successive possono utilizzare un file OverlayConfig ( config.xml ) invece degli attributi manifest. L'utilizzo di un file di sovrapposizione è il metodo consigliato per le sovrapposizioni.

  • Tutti i dispositivi possono utilizzare gli attributi manifest ( android:isStatic e android:priority ) per configurare RRO statici.

Utilizza OverlayConfig

In Android 11 o versioni successive, puoi utilizzare OverlayConfig per configurare la mutabilità, lo stato predefinito e la priorità degli overlay. Per configurare un overlay, creare o modificare il file situato in partition/overlay/config/config.xml , dove partition è la partizione dell'overlay da configurare. Per essere configurato, un overlay deve risiedere nella directory overlay/ della partizione in cui è configurato l'overlay. Il codice seguente mostra un esempio product/overlay/config/config.xml .

<config>
    <merge path="OEM-common-rros-config.xml" />
    <overlay package="com.oem.overlay.device" mutable="false" enabled="true" />
    <overlay package="com.oem.green.theme" enabled="true" />
</config>"

Il tag <overlay> richiede un attributo package che indichi quale pacchetto overlay viene configurato. L'attributo opzionale enabled controlla se l'overlay è abilitato o meno per impostazione predefinita (l'impostazione predefinita è false ). L'attributo facoltativo mutable controlla se l'overlay è modificabile o meno e il suo stato abilitato può essere modificato a livello di codice in fase di esecuzione (l'impostazione predefinita è true ). Gli overlay non elencati in un file di configurazione sono modificabili e disabilitati per impostazione predefinita.

Precedenza di sovrapposizione

Quando più overlay sostituiscono le stesse risorse, l'ordine degli overlay è importante. Una sovrapposizione ha una precedenza maggiore rispetto alle sovrapposizioni con configurazioni che precedono la propria configurazione. L'ordine di precedenza degli overlay nelle diverse partizioni (dalla precedenza minore a quella maggiore) è il seguente.

  • system
  • vendor
  • odm
  • oem
  • product
  • system_ext

Unisci file

L'uso dei tag <merge> consente di unire altri file di configurazione nella posizione specificata nel file di configurazione. L'attributo path del tag rappresenta il percorso del file da unire relativo alla directory contenente i file di configurazione dell'overlay.

Utilizza attributi manifest/RRO statici

In Android 10 o versioni precedenti, l'immutabilità e la precedenza dell'overlay vengono configurate utilizzando i seguenti attributi manifest.

  • android:isStatic . Quando il valore di questo attributo booleano è impostato su true , l'overlay è abilitato per impostazione predefinita ed è immutabile, il che impedisce che l'overlay venga disabilitato.

  • android:priority . Il valore di questo attributo numerico (che influisce solo sugli overlay statici) configura la precedenza dell'overlay quando più overlay statici hanno come target lo stesso valore della risorsa. Un numero più alto indica una precedenza più alta.

Il codice seguente mostra un esempio AndroidManifest.xml .

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.overlay">
    <application android:hasCode="false" />
    <overlay android:targetPackage="com.example.target"
                   android:isStatic="true"
                   android:priority="5"/>
</manifest>

Cambiamenti in Android 11

In Android 11 o versioni successive, se un file di configurazione si trova in partition/overlay/config/config.xml , gli overlay vengono configurati utilizzando quel file e android:isStatic e android:priority non hanno effetto sugli overlay situati nella partizione. La definizione di un file di configurazione di sovrapposizione in qualsiasi partizione impone la precedenza della partizione di sovrapposizione.

Inoltre, Android 11 o versioni successive elimina la possibilità di utilizzare overlay statici per influenzare i valori delle risorse lette durante l'installazione del pacchetto. Per il caso d'uso comune di utilizzo di overlay statici per modificare il valore dei valori booleani che configurano lo stato abilitato del componente, utilizzare il tag <component-override> SystemConfig (nuovo in Android 11).

Sovrapposizioni di debug

Per abilitare, disabilitare e scaricare manualmente gli overlay, utilizzare il seguente comando della shell del gestore overlay.

adb shell cmd overlay

OverlayManagerService utilizza idmap2 per mappare gli ID delle risorse nel pacchetto di destinazione agli ID delle risorse nel pacchetto overlay. Le mappature degli ID generate vengono archiviate in /data/resource-cache/ . Se il tuo overlay non funziona correttamente, trova il file idmap corrispondente per il tuo overlay in /data/resource-cache/ , quindi esegui il comando seguente.

adb shell idmap2 dump --idmap-path [file]

Questo comando stampa la mappatura delle risorse come mostrato di seguito.

[target res id] - > [overlay res id] [resource name]
0x01040151 -> 0x01050001 string/config_dozeComponent
0x01040152 -> 0x01050002 string/config_dozeDoubleTapSensorType
0x01040153 -> 0x01050003 string/config_dozeLongPressSensorType