ランタイム リソース オーバーレイ(RRO)

ランタイム リソース オーバーレイ(RRO)は、実行時にターゲット パッケージのリソース値を変更するパッケージです。たとえば、システム イメージにインストールされているアプリの動作が、リソースの値に基づいて変わる場合があります。そのような場合、ビルド時にリソース値をハードコードするのではなく、別のパーティションにインストールした RRO によって、実行時にアプリのリソースの値を変更できます。

RRO は有効または無効にできます。有効 / 無効状態をプログラムで設定し、RRO によるリソース値の変更機能を切り替えることができます。RRO はデフォルトでは無効になっています(ただし、静的 RRO はデフォルトで有効になっています)。

リソースのオーバーレイ

オーバーレイは、オーバーレイ パッケージで定義されたリソースを、ターゲット パッケージで定義されたリソースにマッピングすることで機能します。アプリがターゲット パッケージ内のリソースの値を解決しようとすると、代わりに、ターゲット リソースのマッピング先であるオーバーレイ リソースの値が返されます。

マニフェストの設定

<manifest> タグの子として <overlay> タグを含むパッケージは、RRO パッケージと見なされます。

  • 必須の android:targetPackage 属性の値は、RRO によってオーバーレイするパッケージの名前を指定します。

  • オプションの android:targetName 属性の値は、RRO によってオーバーレイするターゲット パッケージのオーバーレイ可能なリソース サブセットの名前を指定します。ターゲットでオーバーレイ可能なリソースセットが定義されていない場合、この属性は存在しません。

次のコードは、オーバーレイ 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>

オーバーレイは、コードをオーバーレイできないため、DEX ファイルを含めることはできません。また、マニフェストの <application> タグの android:hasCode 属性を false に設定する必要があります。

リソースマップの定義

Android 11 以降では、オーバーレイ リソースのマップを定義するためのメカニズムとして、オーバーレイ パッケージの res/xml ディレクトリにファイルを作成し、オーバーレイすべきターゲット リソースとその置換値を列挙した後、<overlay> マニフェスト タグの android:resourcesMap 属性の値をリソース マッピング ファイルへの参照に設定するという方法が推奨されます。

次のコードは、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>

次のコードは、オーバーレイ マニフェストの例を示しています。

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

パッケージのビルド

Android 11 以降は、オーバーレイに対して Soong ビルドルールをサポートしています。このルールによって、Android Asset Packaging Tool 2(AAPT2)が、同じ値を持つリソースの設定の重複排除を試みたり(--no-resource-deduping)、デフォルト設定のないリソースを削除したりすること(--no-resource-removal)を防止できます。次のコードは、Android.bp ファイルの例を示しています。

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

リソースの解決

ターゲット リソースまたはオーバーレイ リソースでクエリ対象のリソースに関する設定が複数定義されている場合、リソース ランタイムは、デバイス設定に最も一致する設定の値を返します。最も一致する設定を判断するには、オーバーレイ リソース設定のセットをターゲット リソース設定のセットに統合した後、通常のリソース解決フローが実施されます(詳細については、Android が最適なリソースを見つける仕組みをご覧ください)。

たとえば、オーバーレイが drawable-en 設定の値を定義し、ターゲットが drawable-en-port の値を定義している場合、drawable-en-port のほうがより一致しているので、実行時にはターゲット設定 drawable-en-port の値が選択されます。すべての drawable-en 設定をオーバーレイするには、ターゲットが定義している各 drawable-en 設定の値をオーバーレイでも定義する必要があります。

オーバーレイは自身のリソースを参照できます。動作は、Android のリリースによって異なります。

  • Android 11 以上では、各オーバーレイに独自の予約済みリソース ID スペースがあり、ターゲット リソース ID スペースや他のオーバーレイ リソース ID スペースと重複することがないため、自身のリソースを参照するオーバーレイは正常に機能します。

  • Android 10 以前では、オーバーレイとターゲット パッケージが同じリソース ID スペースを共有するため、@type/name 構文を使用してそれぞれが自身のリソースを参照しようとすると、競合や予期せぬ動作が生じる場合があります。

オーバーレイの有効化 / 無効化

変更可能なオーバーレイを有効または無効にするには OverlayManager API を使用します(Context#getSystemService(Context.OVERLAY_SERVICE) を使用して API インターフェースを取得する)。オーバーレイを有効化できるのは、オーバーレイがターゲットとするパッケージ、または android.permission.CHANGE_OVERLAY_PACKAGES 権限を持つパッケージのみです。オーバーレイを有効または無効にすると、設定変更イベントがターゲット パッケージに伝えられ、ターゲット アクティビティが再起動します。

オーバーレイ可能なリソースの制限

Android 10 以降では、RRO でオーバーレイ可能なリソースのセットを <overlayable> XML タグで公開します。次の res/values/overlayable.xml ファイルの例では、string/foointeger/bar は、デバイスの外観のテーマを設定するためのリソースです。これらのリソースをオーバーレイするには、オーバーレイ可能なリソースのコレクションに名前を付けて、オーバーレイのターゲットとして明示的に指定する必要があります。

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

1 つの APK で複数の <overlayable> タグを定義できますが、各タグの名前はパッケージ内で一意である必要があります。たとえば、次のように定義します。

  • 2 つの異なるパッケージの両方で <overlayable name="foo"> を定義することは可能です。

  • 1 つの APK で 2 つの <overlayable name="foo"> ブロックを定義することはできません。

次のコードは、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>

アプリで <overlayable> タグを定義する場合、そのアプリをターゲットにするオーバーレイには次の特徴があります。

  • targetName を指定する必要があります。

  • <overlayable> タグ内にリストされたリソースのみをオーバーレイできます。

  • 1 つの <overlayable> 名のみをターゲットにすることができます。

パッケージ内でオーバーレイ可能なリソースが公開されていても、android:targetName を使用して特定の <overlayable> タグがターゲットに指定されていなければ、そのパッケージをターゲットとするオーバーレイは有効にできません。

ポリシーの制限

オーバーレイ可能なリソースに制限を適用するには、<policy> タグを使用します。type 属性では、指定されたリソースをオーバーライドするためにオーバーレイで遵守する必要があるポリシーを指定します。サポートされているタイプは次のとおりです。

  • public。任意のオーバーレイでリソースをオーバーライドできます。
  • system。システム パーティション上の任意のオーバーレイでリソースをオーバーライドできます。
  • vendor。ベンダー パーティション上の任意のオーバーレイでリソースをオーバーライドできます。
  • product。プロダクト パーティション上の任意のオーバーレイでリソースをオーバーライドできます。
  • signature。ターゲット APK と同じ署名で署名された任意のオーバーレイでリソースをオーバーライドできます。

次のコードは、res/values/overlayable.xml ファイルの <policy> タグの例を示しています。

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

複数のポリシーを指定するには、区切り文字として縦棒(|)を使用します。複数のポリシーを指定した場合、オーバーレイでは、<policy> タグ内にリストされたリソースをオーバーライドするための 1 つのポリシーのみを遵守する必要があります。

オーバーレイの設定

Android は、Android のリリース バージョンに応じてオーバーレイの可変性、デフォルト状態、優先値を設定するさまざまなメカニズムをサポートしています。

  • Android 11 以降を搭載したデバイスでは、マニフェスト属性の代わりに OverlayConfig ファイル(config.xml)を使用できます。オーバーレイにはオーバーレイ ファイルの使用をおすすめします。

  • すべてのデバイスで、静的 RRO の設定にマニフェスト属性(android:isStaticandroid:priority)を使用できます。

OverlayConfig の使用

Android 11 以降では、OverlayConfig を使用してオーバーレイの可変性、デフォルト状態、優先値を設定できます。オーバーレイを設定するには、partition/overlay/config/config.xml でオーバーレイ ファイルを作成するか、同じ場所のオーバーレイ ファイルを変更します。この場合、partition は、設定対象オーバーレイのパーティションです。設定するには、オーバーレイが設定されているパーティションの overlay/ ディレクトリにオーバーレイが存在している必要があります。次のコードは、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>"

<overlay> タグには、設定対象のオーバーレイ パッケージを示す package 属性が必要です。オプションの enabled 属性は、オーバーレイをデフォルトで有効にするかどうかを制御します(デフォルトは false)。オプションの mutable 属性は、オーバーレイを変更可能にするかどうか、および実行時に有効化状態をプログラムで変更できるかどうかを制御します(デフォルトは true)。構成ファイル内にリストされていないオーバーレイは変更可能であり、デフォルトで無効になっています。

オーバーレイの優先順位

複数のオーバーレイが同じリソースをオーバーライドしている場合は、オーバーレイの順序が重要になります。上位の設定を持つオーバーレイほど優先順位が低くなります。異なるパーティションにあるオーバーレイの優先順位(低いものから順に)は、次のとおりです。

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

ファイルの統合

<merge> タグを使用すると、他の構成ファイルを、構成ファイル内の指定の場所に統合できます。このタグの path 属性は、統合するファイルのパス(オーバーレイ構成ファイルが含まれるディレクトリを基準とする相対パス)を表します。

マニフェスト属性の使用(静的 RRO)

Android 10 以前でオーバーレイの不変性と優先順位を設定するには、次のマニフェスト属性を使用します。

  • android:isStatic。このブール値属性の値が true に設定されている場合、オーバーレイはデフォルトで有効とされ、変更できません。これにより、オーバーレイは無効化できなくなります。

  • android:priority。この数値属性の値(静的なオーバーレイにのみ影響します)は、複数の静的オーバーレイが同じリソース値をターゲットにしている場合のオーバーレイの優先順位を設定します。数値が大きいほど優先度が高くなります。

次のコードは、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>

Android 11 の変更点

Android 11 以降では、構成ファイルが partition/overlay/config/config.xml にある場合、そのファイルを使用してオーバーレイが設定されます。android:isStaticandroid:priority はパーティション内のオーバーレイに影響しません。任意のパーティションでオーバーレイ構成ファイルを定義すると、オーバーレイ パーティションの優先順位が適用されます。

さらに、Android 11 以降では、パッケージのインストール時に読み取られるリソースの値に静的オーバーレイで影響を及ぼす機能が削除されています。一般的なユースケースとしては、コンポーネントの有効化状態を設定するブール値の値を静的オーバーレイで変更する場合、<component-override> SystemConfig タグを使用します(Android 11 の新機能)。

オーバーレイのデバッグ

オーバーレイの有効化、無効化、ダンプを手動で行うには、オーバーレイ マネージャーの次のシェルコマンドを使用します。

adb shell cmd overlay

OverlayManagerServiceidmap2 を使用して、ターゲット パッケージのリソース ID をオーバーレイ パッケージのリソース ID にマッピングします。生成された ID のマッピングは /data/resource-cache/ に格納されます。オーバーレイが正常に機能しない場合は、/data/resource-cache/ でオーバーレイの対応する idmap ファイルを探して、次のコマンドを実行します。

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

このコマンドは、次のようなリソースのマッピングを出力します。

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