Mengubah nilai resource aplikasi saat runtime

Runtime resource overlay (RRO) adalah paket yang mengubah nilai resource paket target saat runtime. Misalnya, aplikasi yang diinstal pada image sistem dapat mengubah perilakunya berdasarkan nilai resource. Daripada melakukan hardcode nilai resource pada waktu build, RRO yang diinstal pada partisi yang berbeda dapat mengubah nilai resource aplikasi saat runtime.

RRO dapat diaktifkan atau dinonaktifkan. Anda dapat menetapkan status aktifkan/nonaktifkan secara terprogram untuk mengalihkan kemampuan RRO guna mengubah nilai resource. RRO dinonaktifkan secara default (tetapi, RRO statis diaktifkan secara default).

Resource overlay

Overlay berfungsi dengan memetakan resource yang ditentukan dalam paket overlay ke resource yang ditetapkan dalam paket target. Saat aplikasi mencoba me-resolve nilai resource dalam paket target, nilai resource overlay yang dipetakan ke resource target akan ditampilkan.

Menyiapkan manifes

Paket dianggap sebagai paket RRO jika berisi tag <overlay> sebagai turunan dari tag <manifest>.

  • Nilai atribut android:targetPackage yang diperlukan menentukan nama paket yang ingin ditempatkan oleh RRO untuk ditempatkan.

  • Nilai atribut android:targetName opsional menentukan nama subset resource yang dapat ditempatkan dari paket target yang ingin ditempatkan oleh RRO. Jika target tidak menentukan kumpulan resource yang dapat ditempatkan, atribut ini tidak boleh ada.

Kode berikut menunjukkan contoh 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>

Overlay tidak dapat menempatkan kode, sehingga tidak dapat memiliki file DEX. Selain itu, atribut android:hasCode dari tag <application> dalam manifes harus ditetapkan ke false.

Menentukan peta resource

Di Android 11 atau yang lebih tinggi, mekanisme yang direkomendasikan untuk menentukan peta resource overlay adalah dengan membuat file dalam direktori res/xml dari paket overlay, mengenumerasi resource target yang harus ditempatkan secara luas dan nilai penggantinya, lalu menetapkan nilai atribut android:resourcesMap tag manifes <overlay> ke referensi ke file pemetaan resource.

Kode berikut menunjukkan contoh file 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>

Kode berikut menunjukkan contoh manifes 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>

Mem-build paket

Android 11 atau yang lebih baru mendukung aturan build Soong untuk overlay yang mencegah Android Asset Packaging Tool 2 (AAPT2) mencoba menghapus konfigurasi resource dengan nilai yang sama (--no-resource-deduping) dan dari menghapus resource tanpa konfigurasi default (--no-resource-removal). Kode berikut menampilkan contoh file Android.bp.

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

Menyelesaikan resource

Jika resource target atau resource overlay memiliki beberapa konfigurasi yang ditentukan untuk resource yang dikueri, runtime resource akan menampilkan nilai konfigurasi yang paling sesuai dengan konfigurasi konfigurasi perangkat. Untuk menentukan konfigurasi mana yang merupakan konfigurasi yang paling cocok, gabungkan kumpulan konfigurasi resource overlay ke dalam kumpulan konfigurasi resource target, lalu ikuti alur resolusi resource reguler (untuk mengetahui detailnya, lihat Cara Android menemukan resource yang paling cocok).

Misalnya, jika overlay menentukan nilai untuk konfigurasi drawable-en dan target menentukan nilai untuk drawable-en-port, drawable-en-port akan memiliki kecocokan yang lebih baik sehingga nilai konfigurasi target drawable-en-port dipilih pada runtime. Untuk menempatkan semua konfigurasi drawable-en, overlay harus menentukan nilai untuk setiap konfigurasi drawable-en yang ditentukan target.

Overlay dapat mereferensikan resource-nya sendiri, dengan perilaku yang berbeda di antara rilis Android.

  • Di Android 11 atau yang lebih tinggi, setiap overlay memiliki ruang ID resource yang dicadangkan sendiri yang tidak tumpang-tindih dengan ruang ID resource target atau ruang ID resource overlay lainnya, sehingga overlay yang mereferensikan resourcenya sendiri berfungsi seperti yang diharapkan.

  • Di Android 10 atau yang lebih lama, overlay dan paket target berbagi ruang ID resource yang sama, yang dapat menyebabkan konflik dan perilaku yang tidak terduga saat mencoba mereferensikan resource-nya sendiri menggunakan sintaksis @type/name.

Mengaktifkan/menonaktifkan overlay

Gunakan OverlayManager API untuk mengaktifkan dan menonaktifkan overlay yang dapat diubah (ambil antarmuka API menggunakan Context#getSystemService(Context.OVERLAY_SERVICE)). Overlay hanya dapat diaktifkan oleh paket yang ditargetkannya atau oleh paket dengan izin android.permission.CHANGE_OVERLAY_PACKAGES. Saat overlay diaktifkan atau dinonaktifkan, peristiwa perubahan konfigurasi akan diterapkan ke paket target dan aktivitas target akan diluncurkan ulang.

Membatasi resource yang dapat ditempatkan

Di Android 10 atau yang lebih tinggi, tag XML <overlayable> mengekspos kumpulan resource yang diizinkan untuk ditempatkan oleh RRO. Pada contoh file res/values/overlayable.xml berikut, string/foo dan integer/bar adalah resource yang digunakan untuk memberi tema tampilan perangkat; untuk menempatkan resource ini, overlay harus secara eksplisit menargetkan kumpulan resource yang dapat ditempatkan menurut nama.

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

APK dapat menentukan beberapa tag <overlayable>, tetapi setiap tag harus memiliki nama unik dalam paket. Misalnya, nilainya adalah:

  • Mengizinkan dua paket yang berbeda untuk menentukan <overlayable name="foo">.

  • Tidak disarankan untuk satu APK memiliki dua blok <overlayable name="foo">.

Kode berikut menunjukkan contoh overlay dalam 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>

Saat aplikasi menentukan tag <overlayable>, overlay yang menargetkan aplikasi tersebut:

  • Harus menentukan targetName.

  • Hanya dapat menempatkan resource yang tercantum dalam tag <overlayable>.

  • Hanya dapat menargetkan satu nama <overlayable>.

Anda tidak dapat mengaktifkan overlay yang menargetkan paket yang mengekspos resource yang dapat di-overlay, tetapi tidak menggunakan android:targetName untuk menargetkan tag <overlayable> tertentu.

Kebijakan pembatasan

Gunakan tag <policy> untuk menerapkan batasan pada resource yang dapat ditempatkan. Atribut type menentukan kebijakan yang harus dipenuhi overlay untuk mengganti resource yang disertakan. Jenis yang didukung meliputi hal berikut.

  • public. Overlay apa pun dapat mengganti resource.
  • system. Overlay apa pun di partisi sistem dapat mengganti resource.
  • vendor. Overlay apa pun di partisi vendor dapat mengganti resource.
  • product. Setiap overlay pada partisi produk dapat mengganti resource.
  • oem. Overlay apa pun di partisi oem dapat mengganti resource.
  • odm. Overlay pada partisi odm dapat mengganti resource.
  • signature. Overlay apa pun yang ditandatangani dengan tanda tangan yang sama dengan APK target dapat mengganti resource.
  • actor. Overlay apa pun yang ditandatangani dengan tanda tangan yang sama dengan APK actor dapat mengganti resource. Pelaku dideklarasikan dalam tag Rename-actor dalam konfigurasi sistem.
  • config_signature. Setiap overlay yang ditandatangani dengan tanda tangan yang sama dengan apk overlay-config dapat mengganti resource. Konfigurasi overlay dideklarasikan dalam tag overlay-config-signature di konfigurasi sistem.

Kode berikut menunjukkan contoh tag <policy> dalam 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>

Untuk menentukan beberapa kebijakan, gunakan garis vertikal (|) sebagai karakter pemisah. Jika beberapa kebijakan ditentukan, overlay hanya perlu memenuhi satu kebijakan untuk mengganti resource yang tercantum dalam tag <policy>.

Mengonfigurasi overlay

Android mendukung berbagai mekanisme untuk mengonfigurasi mutabilitas, status default, dan prioritas overlay, bergantung pada versi rilis Android.

  • Perangkat yang menjalankan Android 11 atau yang lebih tinggi dapat menggunakan file OverlayConfig (config.xml), bukan atribut manifes. Menggunakan file overlay adalah metode yang direkomendasikan untuk overlay.

  • Semua perangkat dapat menggunakan atribut manifes (android:isStatic dan android:priority) untuk mengonfigurasi RRO statis.

Menggunakan OverlayConfig

Di Android 11 atau yang lebih tinggi, Anda dapat menggunakan OverlayConfig untuk mengonfigurasi mutabilitas, status default, dan prioritas overlay. Untuk mengonfigurasi overlay, buat atau ubah file yang terletak di partition/overlay/config/config.xml, dengan partition adalah partisi overlay yang akan dikonfigurasi. Agar dapat dikonfigurasi, overlay harus berada di direktori overlay/ partisi tempat overlay dikonfigurasi. Kode berikut menunjukkan contoh 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>"

Tag <overlay> memerlukan atribut package yang menunjukkan paket overlay mana yang sedang dikonfigurasi. Atribut enabled opsional mengontrol apakah overlay diaktifkan secara default atau tidak (defaultnya adalah false). Atribut mutable opsional mengontrol apakah overlay dapat diubah atau tidak dan dapat mengubah status pengaktifannya secara terprogram saat runtime (defaultnya adalah true). Overlay yang tidak tercantum dalam file konfigurasi dapat diubah dan dinonaktifkan secara default.

Prioritas overlay

Jika beberapa overlay mengganti resource yang sama, urutan overlay sangat penting. Overlay memiliki prioritas yang lebih besar daripada overlay dengan konfigurasi yang mendahului konfigurasinya sendiri. Urutan prioritas overlay dalam berbagai partisi (dari prioritas paling kecil hingga terbesar) adalah sebagai berikut.

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

Gabungkan file

Penggunaan tag <merge> memungkinkan file konfigurasi lainnya digabungkan pada posisi yang ditentukan ke dalam file konfigurasi. Atribut path pada tag mewakili jalur file yang akan digabungkan, secara relatif terhadap direktori yang berisi file konfigurasi overlay.

Menggunakan atribut manifes/RRO statis

Di Android 10 atau yang lebih lama, ketidakmutlakan dan prioritas overlay dikonfigurasi menggunakan atribut manifes berikut.

  • android:isStatic. Jika nilai atribut boolean ini ditetapkan ke true, overlay diaktifkan secara default dan tidak dapat diubah, sehingga mencegah overlay dinonaktifkan.

  • android:priority. Nilai atribut numerik ini (yang hanya memengaruhi overlay statis) mengonfigurasi prioritas overlay saat beberapa overlay statis menargetkan nilai resource yang sama. Angka yang lebih tinggi menunjukkan prioritas yang lebih tinggi.

Kode berikut menunjukkan contoh 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>

Perubahan di Android 11

Di Android 11 atau yang lebih tinggi, jika file konfigurasi berada di partition/overlay/config/config.xml, overlay dikonfigurasi menggunakan file tersebut dan android:isStatic serta android:priority tidak berpengaruh pada overlay yang berada di partisi. Menentukan file konfigurasi overlay di partisi mana pun akan menerapkan prioritas partisi overlay.

Selain itu, Android 11 atau yang lebih tinggi menghapus kemampuan untuk menggunakan overlay statis guna memengaruhi nilai resource yang dibaca selama penginstalan paket. Untuk kasus penggunaan umum penggunaan overlay statis guna mengubah nilai boolean yang mengonfigurasi status pengaktifan komponen, gunakan tag <component-override> SystemConfig (baru di Android 11).

Overlay debug

Untuk mengaktifkan, menonaktifkan, dan membuang overlay secara manual, gunakan perintah shell pengelola overlay berikut.

adb shell cmd overlay

OverlayManagerService menggunakan idmap2 untuk memetakan ID resource dalam paket target ke ID resource dalam paket overlay. Pemetaan ID yang dihasilkan disimpan di /data/resource-cache/. Jika overlay tidak berfungsi dengan benar, cari file idmap yang sesuai untuk overlay Anda di /data/resource-cache/, lalu jalankan perintah berikut.

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

Perintah ini mencetak pemetaan resource seperti yang ditunjukkan di bawah ini.

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