Modifier la valeur des ressources d'une application au moment de l'exécution

Une superposition de ressources d'exécution (RRO) est un package qui modifie les valeurs de ressources d'un package cible au moment de l'exécution. Par exemple, une application installée sur l'image système peut modifier son comportement en fonction de la valeur d'une ressource. Plutôt que de coder en dur la valeur des ressources au moment de la construction, un RRO installé sur une partition différente peut modifier les valeurs des ressources de l'application au moment de l'exécution.

Les RRO peuvent être activés ou désactivés. Vous pouvez définir par programme l’état d’activation/désactivation pour activer/désactiver la capacité d’un RRO à modifier les valeurs des ressources. Les RRO sont désactivés par défaut (cependant, les RRO statiques sont activés par défaut).

Ressources de superposition

Les superpositions fonctionnent en mappant les ressources définies dans le package de superposition aux ressources définies dans le package cible. Lorsqu'une application tente de résoudre la valeur d'une ressource dans le package cible, la valeur de la ressource de superposition à laquelle la ressource cible est mappée est renvoyée à la place.

Configurer le manifeste

Un package est considéré comme un package RRO s’il contient une balise <overlay> en tant qu’enfant de la balise <manifest> .

  • La valeur de l'attribut android:targetPackage requis spécifie le nom du package que le RRO a l'intention de superposer.

  • La valeur de l'attribut facultatif android:targetName spécifie le nom du sous-ensemble de ressources superposables du package cible que le RRO a l'intention de superposer. Si la cible ne définit pas un ensemble de ressources superposables, cet attribut ne doit pas être présent.

Le code suivant montre un exemple de superposition 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>

Les superpositions ne peuvent pas superposer du code, elles ne peuvent donc pas contenir de fichiers DEX. De plus, l'attribut android:hasCode de la balise <application > dans le manifeste doit être défini sur false .

Définir la cartographie des ressources

Sous Android 11 ou version ultérieure, le mécanisme recommandé pour définir la carte des ressources de superposition consiste à créer un fichier dans le répertoire res/xml du package de superposition, à énumérer les ressources cibles qui doivent être superposées et leurs valeurs de remplacement, puis à définir la valeur de Attribut android:resourcesMap de la balise manifeste <overlay> à une référence au fichier de mappage de ressources.

Le code suivant montre un exemple de fichier res/xml/overlays.xml .

<?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>

Le code suivant montre un exemple de manifeste de superposition.

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

Construire le package

Android 11 ou version ultérieure prend en charge une règle de construction Soong pour les superpositions qui empêche Android Asset Packaging Tool 2 (AAPT2) de tenter de dédoublonner les configurations de ressources avec la même valeur ( --no-resource-deduping ) et de supprimer des ressources sans configurations par défaut ( --no-resource-removal ). Le code suivant montre un exemple de fichier Android.bp .

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

Résoudre les ressources

Si une ressource cible ou une ressource de superposition a plusieurs configurations définies pour la ressource interrogée, le runtime des ressources renvoie la valeur de la configuration qui correspond le mieux à la configuration de l'appareil. Pour déterminer quelle configuration correspond le mieux à la configuration, fusionnez l'ensemble des configurations de ressources de superposition dans l'ensemble des configurations de ressources cibles, puis suivez le flux de résolution de ressources régulier (pour plus de détails, reportez-vous à Comment Android trouve la ressource la mieux correspondante ).

Par exemple, si une superposition définit une valeur pour la configuration drawable-en et que la cible définit une valeur pour drawable-en-port , drawable-en-port a une meilleure correspondance, donc la valeur de la configuration cible drawable-en-port est choisi au moment de l’exécution. Pour superposer toutes les configurations drawable-en , la superposition doit définir une valeur pour chaque configuration drawable-en définie par la cible.

Les superpositions peuvent référencer leurs propres ressources, avec des comportements différents selon les versions d'Android.

  • Dans Android 11 ou version ultérieure, chaque superposition possède son propre espace d'ID de ressource réservé qui ne chevauche pas l'espace d'ID de ressource cible ou d'autres espaces d'ID de ressource de superposition, de sorte que les superpositions faisant référence à leurs propres ressources fonctionnent comme prévu.

  • Dans Android 10 ou version antérieure, les superpositions et les packages cibles partagent le même espace d'ID de ressource, ce qui peut provoquer des collisions et un comportement inattendu lorsqu'ils tentent de référencer leurs propres ressources à l'aide de la syntaxe @type/name .

Activer/désactiver les superpositions

Utilisez l'API OverlayManager pour activer et désactiver les superpositions mutables (récupérez l'interface API à l'aide de Context#getSystemService(Context.OVERLAY_SERVICE) ). Une superposition ne peut être activée que par le package qu'elle cible ou par un package disposant de l'autorisation android.permission.CHANGE_OVERLAY_PACKAGES . Lorsqu'une superposition est activée ou désactivée, les événements de modification de configuration se propagent au package cible et les activités cibles sont relancées.

Restreindre les ressources superposables

Sous Android 10 ou version ultérieure, la balise XML <overlayable> expose un ensemble de ressources que les RRO sont autorisés à superposer. Dans l'exemple de fichier res/values/overlayable.xml suivant, string/foo et integer/bar sont des ressources utilisées pour thématiser l'apparence de l'appareil ; pour superposer ces ressources, une superposition doit cibler explicitement la collection de ressources superposables par nom.

<!-- 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 peut définir plusieurs balises <overlayable> , mais chaque balise doit avoir un nom unique dans le package. Par exemple, c'est :

  • OK pour que deux packages différents définissent tous deux <overlayable name="foo"> .

  • Ce n'est pas acceptable qu'un seul APK ait deux blocs <overlayable name="foo"> .

Le code suivant montre un exemple de superposition dans le fichier 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>

Lorsqu'une application définit une balise <overlayable> , les superpositions ciblant cette application :

  • Doit spécifier targetName .

  • Peut superposer uniquement les ressources répertoriées dans la balise <overlayable> .

  • Ne peut cibler qu'un seul nom <overlayable> .

Vous ne pouvez pas activer une superposition ciblant un package qui expose des ressources superposables mais n'utilise pas android:targetName pour cibler une balise <overlayable> spécifique.

Restreindre les politiques

Utilisez la balise <policy> pour appliquer des restrictions sur les ressources superposables. L'attribut type spécifie les stratégies qu'une superposition doit remplir pour remplacer les ressources incluses. Les types pris en charge sont les suivants.

  • public . Toute superposition peut remplacer la ressource.
  • system . Toute superposition sur la partition système peut remplacer les ressources.
  • vendor . Toute superposition sur la partition fournisseur peut remplacer les ressources.
  • product . Toute superposition sur la partition du produit peut remplacer les ressources.
  • oem . Toute superposition sur la partition OEM peut remplacer les ressources.
  • odm . Toute superposition sur la partition odm peut remplacer les ressources.
  • signature . Toute superposition signée avec la même signature que l'APK cible peut remplacer les ressources.
  • actor . Toute superposition signée avec la même signature que l' acteur APK peut remplacer les ressources. L'acteur est déclaré dans la balise d'acteur nommé dans la configuration du système.
  • config_signature . Toute superposition signée avec la même signature que l'apk overlay-config peut remplacer les ressources. L'overlay-config est déclaré dans la balise overlay-config-signature dans la configuration du système.

Le code suivant montre un exemple de balise <policy> dans le fichier 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>

Pour spécifier plusieurs stratégies, utilisez des barres verticales (|) comme caractères de séparation. Lorsque plusieurs stratégies sont spécifiées, une superposition ne doit remplir qu’une seule stratégie pour remplacer les ressources répertoriées dans la balise <policy> .

Configurer les superpositions

Android prend en charge différents mécanismes pour configurer la mutabilité, l'état par défaut et la priorité des superpositions en fonction de la version d'Android.

  • Les appareils exécutant Android 11 ou version ultérieure peuvent utiliser un fichier OverlayConfig ( config.xml ) au lieu des attributs du manifeste. L'utilisation d'un fichier de superposition est la méthode recommandée pour les superpositions.

  • Tous les appareils peuvent utiliser des attributs de manifeste ( android:isStatic et android:priority ) pour configurer des RRO statiques.

Utiliser OverlayConfig

Sous Android 11 ou version ultérieure, vous pouvez utiliser OverlayConfig pour configurer la mutabilité, l'état par défaut et la priorité des superpositions. Pour configurer une superposition, créez ou modifiez le fichier situé dans partition/overlay/config/config.xml , où partition est la partition de la superposition à configurer. Pour être configurée, une superposition doit résider dans le répertoire overlay/ de la partition dans laquelle la superposition est configurée. Le code suivant montre un exemple 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>"

La balise <overlay> nécessite un attribut package qui indique quel package de superposition est en cours de configuration. L'attribut facultatif enabled contrôle si la superposition est activée ou non par défaut (la valeur par défaut est false ). L'attribut mutable facultatif contrôle si la superposition est mutable ou non et peut voir son état activé modifié par programme au moment de l'exécution (la valeur par défaut est true ). Les superpositions non répertoriées dans un fichier de configuration sont modifiables et désactivées par défaut.

Priorité de superposition

Lorsque plusieurs superpositions remplacent les mêmes ressources, l’ordre des superpositions est important. Une superposition a une plus grande priorité que les superpositions dont les configurations précèdent sa propre configuration. L'ordre de priorité des superpositions dans les différentes partitions (de la priorité la plus faible à la plus grande) est le suivant.

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

Fusionner des fichiers

L'utilisation des balises <merge> permet de fusionner d'autres fichiers de configuration à la position spécifiée dans le fichier de configuration. L'attribut path de la balise représente le chemin du fichier à fusionner par rapport au répertoire contenant les fichiers de configuration de superposition.

Utiliser des attributs de manifeste/RRO statiques

Dans Android 10 ou version antérieure, l'immuabilité et la priorité de la superposition sont configurées à l'aide des attributs de manifeste suivants.

  • android:isStatic . Lorsque la valeur de cet attribut booléen est définie sur true , la superposition est activée par défaut et est immuable, ce qui empêche sa désactivation.

  • android:priority . La valeur de cet attribut numérique (qui affecte uniquement les superpositions statiques) configure la priorité de la superposition lorsque plusieurs superpositions statiques ciblent la même valeur de ressource. Un nombre plus élevé indique une priorité plus élevée.

Le code suivant montre un exemple 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>

Changements dans Android 11

Sous Android 11 ou version ultérieure, si un fichier de configuration se trouve dans partition/overlay/config/config.xml , les superpositions sont configurées à l'aide de ce fichier et android:isStatic et android:priority n'ont pas d'effet sur les superpositions situées dans la partition. La définition d'un fichier de configuration de superposition dans n'importe quelle partition applique la priorité de la partition de superposition.

De plus, Android 11 ou version ultérieure supprime la possibilité d'utiliser des superpositions statiques pour affecter les valeurs des ressources lues lors de l'installation du package. Pour le cas d'utilisation courant de l'utilisation de superpositions statiques pour modifier la valeur des booléens qui configurent l'état activé des composants, utilisez la balise <component-override> SystemConfig (nouvelle dans Android 11).

Superpositions de débogage

Pour activer, désactiver et vider manuellement les superpositions, utilisez la commande shell du gestionnaire de superposition suivante.

adb shell cmd overlay

OverlayManagerService utilise idmap2 pour mapper les ID de ressources du package cible aux ID de ressources du package de superposition. Les mappages d'ID générés sont stockés dans /data/resource-cache/ . Si votre superposition ne fonctionne pas correctement, recherchez le fichier idmap correspondant à votre superposition dans /data/resource-cache/ , puis exécutez la commande suivante.

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

Cette commande imprime le mappage des ressources comme indiqué ci-dessous.

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