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

Une superposition de ressources d'exécution (RRO) est un package qui modifie les valeurs de ressource d'un package cible lors 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 de la ressource 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 programmation 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).

Superposer des ressources

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.

Mise en place du manifeste

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

  • La valeur de l'attribut obligatoire android:targetPackage 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 superposable de ressources 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 le code, elles ne peuvent donc pas avoir de fichiers DEX. De plus, l'attribut android:hasCode de la <application > dans le manifeste doit être défini sur false .

Définir la carte des ressources

Dans Android 11 ou supérieur, le mécanisme recommandé pour définir la carte des ressources de superposition est de créer un fichier dans le répertoire res/xml du package de superposition, d'énumérer les ressources cibles qui doivent être superposées et leurs valeurs de remplacement, puis de définir la valeur du android:resourcesMap attribut 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 paquet

Android 11 ou supérieur 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édupliquer des 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ésolution des ressources

Si une ressource cible ou une ressource de superposition a plusieurs configurations définies pour la ressource interrogée, l'environnement d'exécution des ressources renvoie la valeur de la configuration qui correspond le mieux à la configuration de 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 standard (pour plus de détails, reportez-vous à Comment Android trouve la ressource la mieux adaptée ).

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, de sorte que la valeur de la configuration cible drawable-en-port est choisi à 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 entre 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 modifiables (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 avec l'autorisation android.permission.CHANGE_OVERLAY_PACKAGES . Lorsqu'une superposition est activée ou désactivée, les événements de changement de configuration se propagent au package cible et relancent les activités de la cible.

Restreindre les ressources superposables

Dans Android 10 ou supérieur, 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 , 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 leur 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 les deux <overlayable name="foo"> .

  • Ce n'est pas OK pour un seul APK d'avoir deux <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 <overlayable> , les superpositions ciblant cette application :

  • Doit spécifier targetName .

  • Ne peut superposer que les ressources répertoriées dans la <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 <overlayable> spécifique.

Politiques de restriction

Utilisez la <policy> pour appliquer des restrictions sur les ressources superposables. L'attribut type spécifie les politiques qu'une superposition doit respecter pour remplacer les ressources incluses. Les types pris en charge incluent 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'APK de l' acteur peut remplacer les ressources. L'acteur est déclaré dans la balise d' acteur nommé dans la configuration système.
  • config_signature . Toute superposition signée avec la même signature que l' apk overlay-config peut remplacer les ressources. La superposition-config est déclarée dans la balise overlay-config-signature dans la configuration système.

Le code suivant montre un exemple de <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 n'a besoin de remplir qu'une seule stratégie pour remplacer les ressources répertoriées dans la <policy> .

Configuration des 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 d'attributs manifestes. L'utilisation d'un fichier de superposition est la méthode recommandée pour les superpositions.

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

Utilisation de OverlayConfig

Dans Android 11 ou supérieur, 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 <overlay> nécessite un attribut de 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 son état activé peut être 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 avec des configurations précédant sa propre configuration. L'ordre de priorité des superpositions dans différentes partitions (de la plus petite à la plus grande priorité) est le suivant.

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

Fusionner des fichiers

L'utilisation de 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 la superposition.

Utilisation d'attributs manifestes (RRO statiques)

Dans Android 10 ou une 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 la superposition d'être désactivée.

  • android:priority . La valeur de cet attribut numérique (qui n'affecte que 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

Dans Android 11 ou supérieur, 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 supérieur 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 consistant à utiliser des superpositions statiques pour modifier la valeur des booléens qui configurent l'état activé du composant, utilisez la balise SystemConfig <component-override> (nouveau dans Android 11).

Débogage des superpositions

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 ressource dans le package cible aux ID de ressource dans le 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