Alterar o valor dos recursos de um aplicativo em tempo de execução

Uma sobreposição de recursos de tempo de execução (RRO) é um pacote que altera os valores de recursos de um pacote de destino em tempo de execução. Por exemplo, um aplicativo instalado na imagem do sistema pode alterar seu comportamento com base no valor de um recurso. Em vez de codificar o valor do recurso em tempo de construção, um RRO instalado em uma partição diferente pode alterar os valores dos recursos do aplicativo em tempo de execução.

Os RROs podem ser ativados ou desativados. Você pode definir programaticamente o estado de ativação/desativação para alternar a capacidade de um RRO de alterar valores de recursos. Os RROs são desabilitados por padrão (no entanto, os RROs estáticos são habilitados por padrão).

Recursos de sobreposição

As sobreposições funcionam mapeando recursos definidos no pacote de sobreposição para recursos definidos no pacote de destino. Quando um aplicativo tenta resolver o valor de um recurso no pacote de destino, o valor do recurso de sobreposição para o qual o recurso de destino está mapeado é retornado.

Configure o manifesto

Um pacote é considerado um pacote RRO se contiver uma tag <overlay> como filha da tag <manifest> .

  • O valor do atributo android:targetPackage obrigatório especifica o nome do pacote que o RRO pretende sobrepor.

  • O valor do atributo opcional android:targetName especifica o nome do subconjunto sobreposto de recursos do pacote de destino que o RRO pretende sobrepor. Se o destino não definir um conjunto de recursos sobrepostos, esse atributo não deverá estar presente.

O código a seguir mostra um exemplo de sobreposição 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>

As sobreposições não podem sobrepor código, portanto não podem ter arquivos DEX. Além disso, o atributo android:hasCode da tag <application > no manifesto deve ser definido como false .

Defina o mapa de recursos

No Android 11 ou superior, o mecanismo recomendado para definir o mapa de recursos de sobreposição é criar um arquivo no diretório res/xml do pacote de sobreposição, enumerar os recursos de destino que devem ser sobrepostos e seus valores de substituição e, em seguida, definir o valor do atributo android:resourcesMap da tag de manifesto <overlay> para uma referência ao arquivo de mapeamento de recursos.

O código a seguir mostra um exemplo de arquivo 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>

O código a seguir mostra um exemplo de manifesto de sobreposição.

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

Construa o pacote

O Android 11 ou superior oferece suporte a uma regra de compilação Soong para sobreposições que impede que o Android Asset Packaging Tool 2 (AAPT2) tente desduplicar configurações de recursos com o mesmo valor ( --no-resource-deduping ) e remova recursos sem configurações padrão ( --no-resource-removal ). O código a seguir mostra um exemplo de arquivo Android.bp .

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

Resolver recursos

Se um recurso de destino ou recurso de sobreposição tiver diversas configurações definidas para o recurso que está sendo consultado, o tempo de execução dos recursos retornará o valor da configuração que melhor corresponde à configuração do dispositivo. Para determinar qual configuração é a que melhor corresponde, mescle o conjunto de configurações de recursos de sobreposição no conjunto de configurações de recursos de destino e siga o fluxo regular de resolução de recursos (para obter detalhes, consulte Como o Android encontra o recurso de melhor correspondência ).

Por exemplo, se uma sobreposição definir um valor para a configuração drawable-en e o destino definir um valor para drawable-en-port , drawable-en-port terá uma correspondência melhor, portanto o valor da configuração de destino drawable-en-port será escolhido em tempo de execução. Para sobrepor todas as configurações drawable-en , a sobreposição deve definir um valor para cada configuração drawable-en que o destino define.

As sobreposições podem fazer referência a seus próprios recursos, com comportamentos diferentes entre as versões do Android.

  • No Android 11 ou superior, cada sobreposição tem seu próprio espaço de ID de recurso reservado que não se sobrepõe ao espaço de ID de recurso de destino ou a outros espaços de ID de recurso de sobreposição, portanto, as sobreposições que fazem referência a seus próprios recursos funcionam conforme o esperado.

  • No Android 10 ou versões anteriores, as sobreposições e os pacotes de destino compartilham o mesmo espaço de ID de recurso, o que pode causar colisões e comportamentos inesperados quando tentam referenciar seus próprios recursos usando a sintaxe @type/name .

Ativar/desativar sobreposições

Use a API OverlayManager para ativar e desativar sobreposições mutáveis ​​(recupere a interface da API usando Context#getSystemService(Context.OVERLAY_SERVICE) ). Uma sobreposição pode ser habilitada apenas pelo pacote a que se destina ou por um pacote com a permissão android.permission.CHANGE_OVERLAY_PACKAGES . Quando uma sobreposição é habilitada ou desabilitada, os eventos de mudança de configuração se propagam para o pacote de destino e as atividades de destino são reiniciadas.

Restringir recursos sobrepostos

No Android 10 ou superior, a tag XML <overlayable> expõe um conjunto de recursos que os RROs podem sobrepor. No exemplo de arquivo res/values/overlayable.xml a seguir, string/foo e integer/bar são recursos usados ​​para definir o tema da aparência do dispositivo; para sobrepor esses recursos, uma sobreposição deve visar explicitamente a coleção de recursos sobrepostos por 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>

Um APK pode definir várias tags <overlayable> , mas cada tag deve ter um nome exclusivo no pacote. Por exemplo, é:

  • OK para dois pacotes diferentes definirem <overlayable name="foo"> .

  • Não é aceitável que um único APK tenha dois blocos <overlayable name="foo"> .

O código a seguir mostra um exemplo de sobreposição no arquivo 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 um aplicativo define uma tag <overlayable> , as sobreposições direcionadas a esse aplicativo:

  • Deve especificar targetName .

  • Pode sobrepor apenas os recursos listados na tag <overlayable> .

  • Pode direcionar apenas um nome <overlayable> .

Você não pode ativar uma sobreposição direcionada a um pacote que expõe recursos sobrepostos, mas não usa android:targetName para direcionar uma tag <overlayable> específica.

Restringir políticas

Use a tag <policy> para impor restrições a recursos sobrepostos. O atributo type especifica quais políticas uma sobreposição deve cumprir para substituir os recursos incluídos. Os tipos suportados incluem o seguinte.

  • public . Qualquer sobreposição pode substituir o recurso.
  • system . Qualquer sobreposição na partição do sistema pode substituir os recursos.
  • vendor . Qualquer sobreposição na partição do fornecedor pode substituir os recursos.
  • product . Qualquer sobreposição na partição do produto pode substituir os recursos.
  • oem . Qualquer sobreposição na partição OEM pode substituir os recursos.
  • odm . Qualquer sobreposição na partição odm pode substituir os recursos.
  • signature . Qualquer sobreposição assinada com a mesma assinatura do APK de destino pode substituir os recursos.
  • actor . Qualquer sobreposição assinada com a mesma assinatura do APK do ator pode substituir os recursos. O ator é declarado na tag Named-Actor na configuração do sistema.
  • config_signature . Qualquer sobreposição assinada com a mesma assinatura do apk overlay-config pode substituir os recursos. O overlay-config é declarado na tag overlay-config-signature na configuração do sistema.

O código a seguir mostra um exemplo de tag <policy> no arquivo 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>

Para especificar diversas políticas, use barras verticais (|) como caracteres separadores. Quando diversas políticas são especificadas, uma sobreposição precisa atender apenas uma política para substituir os recursos listados na tag <policy> .

Configurar sobreposições

O Android oferece suporte a diferentes mecanismos para configurar a mutabilidade, o estado padrão e a prioridade das sobreposições, dependendo da versão do Android.

  • Dispositivos com Android 11 ou superior podem usar um arquivo OverlayConfig ( config.xml ) em vez de atributos de manifesto. Usar um arquivo de sobreposição é o método recomendado para sobreposições.

  • Todos os dispositivos podem usar atributos de manifesto ( android:isStatic e android:priority ) para configurar RROs estáticos.

Usar OverlayConfig

No Android 11 ou superior, você pode usar OverlayConfig para configurar a mutabilidade, o estado padrão e a prioridade das sobreposições. Para configurar uma sobreposição, crie ou modifique o arquivo localizado em partition/overlay/config/config.xml , onde partition é a partição da sobreposição a ser configurada. Para ser configurada, uma sobreposição deve residir no diretório overlay/ da partição na qual a sobreposição está configurada. O código a seguir mostra um exemplo 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>"

A tag <overlay> requer um atributo package que indica qual pacote de sobreposição está sendo configurado. O atributo opcional enabled controla se a sobreposição é habilitada ou não por padrão (o padrão é false ). O atributo mutable opcional controla se a sobreposição é mutável ou não e pode ter seu estado habilitado alterado programaticamente em tempo de execução (o padrão é true ). As sobreposições não listadas em um arquivo de configuração são mutáveis ​​e desabilitadas por padrão.

Precedência de sobreposição

Quando diversas sobreposições substituem os mesmos recursos, a ordem das sobreposições é importante. Uma sobreposição tem maior precedência do que sobreposições com configurações anteriores à sua própria configuração. A ordem de precedência das sobreposições em diferentes partições (da menor para a maior precedência) é a seguinte.

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

Mesclar arquivos

O uso de tags <merge> permite que outros arquivos de configuração sejam mesclados na posição especificada no arquivo de configuração. O atributo path da tag representa o caminho do arquivo a ser mesclado em relação ao diretório que contém os arquivos de configuração de sobreposição.

Use atributos de manifesto/RROs estáticos

No Android 10 ou versões anteriores, a imutabilidade e a precedência da sobreposição são configuradas usando os seguintes atributos de manifesto.

  • android:isStatic . Quando o valor deste atributo booleano é definido como true , a sobreposição é habilitada por padrão e é imutável, o que evita que a sobreposição seja desabilitada.

  • android:priority . O valor deste atributo numérico (que afeta apenas sobreposições estáticas) configura a precedência da sobreposição quando diversas sobreposições estáticas têm como destino o mesmo valor de recurso. Um número mais alto indica uma precedência mais alta.

O código a seguir mostra um exemplo 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>

Mudanças no Android 11

No Android 11 ou superior, se um arquivo de configuração estiver localizado em partition/overlay/config/config.xml , as sobreposições serão configuradas usando esse arquivo e android:isStatic e android:priority não terão efeito nas sobreposições localizadas na partição. Definir um arquivo de configuração de sobreposição em qualquer partição impõe a precedência da partição de sobreposição.

Além disso, o Android 11 ou superior elimina a capacidade de usar sobreposições estáticas para afetar os valores dos recursos lidos durante a instalação do pacote. Para o caso de uso comum de uso de sobreposições estáticas para alterar o valor de booleanos que configuram o estado ativado do componente, use a tag SystemConfig <component-override> (nova no Android 11).

Sobreposições de depuração

Para ativar, desativar e despejar sobreposições manualmente, use o seguinte comando shell do gerenciador de sobreposições.

adb shell cmd overlay

OverlayManagerService usa idmap2 para mapear IDs de recursos no pacote de destino para IDs de recursos no pacote de sobreposição. Os mapeamentos de ID gerados são armazenados em /data/resource-cache/ . Se sua sobreposição não estiver funcionando corretamente, encontre o arquivo idmap correspondente para sua sobreposição em /data/resource-cache/ e execute o comando a seguir.

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

Este comando imprime o mapeamento de recursos conforme mostrado abaixo.

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