रनटाइम के दौरान किसी ऐप्लिकेशन के संसाधनों की वैल्यू बदलना

रनटाइम रिसॉर्स ओवरले (आरआरओ) एक ऐसा पैकेज होता है जो रनटाइम के दौरान, टारगेट पैकेज के रिसॉर्स की वैल्यू बदलता है. उदाहरण के लिए, सिस्टम इमेज पर इंस्टॉल किया गया कोई ऐप्लिकेशन, संसाधन की वैल्यू के आधार पर अपना व्यवहार बदल सकता है. बिल्ड टाइम में संसाधन की वैल्यू को हार्डकोड करने के बजाय, किसी दूसरे पार्टीशन पर इंस्टॉल किया गया RRO, रनटाइम में ऐप्लिकेशन के संसाधनों की वैल्यू बदल सकता है.

RRO को चालू या बंद किया जा सकता है. प्रोग्राम के हिसाब से, RRO की चालू/बंद स्थिति सेट की जा सकती है. इससे RRO की, संसाधन की वैल्यू बदलने की क्षमता को टॉगल किया जा सकता है. आरआरओ डिफ़ॉल्ट रूप से बंद होते हैं. हालांकि, स्टैटिक आरआरओ डिफ़ॉल्ट रूप से चालू होते हैं.

ओवरले संसाधन

ओवरले, ओवरले पैकेज में तय किए गए संसाधनों को टारगेट पैकेज में तय किए गए संसाधनों से मैप करके काम करते हैं. जब कोई ऐप्लिकेशन, टारगेट पैकेज में मौजूद किसी संसाधन की वैल्यू को हल करने की कोशिश करता है, तो टारगेट संसाधन के बजाय, टारगेट संसाधन से मैप किए गए ओवरले संसाधन की वैल्यू दिखती है.

मेनिफ़ेस्ट सेट अप करना

किसी पैकेज को RRO पैकेज तब माना जाता है, जब उसमें <manifest> टैग के चाइल्ड के तौर पर <overlay> टैग मौजूद हो.

  • ज़रूरी 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 ऐसेट पैकेजिंग टूल 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 या इसके बाद के वर्शन में, हर ओवरले के लिए एक आरक्षित रिसॉर्स आईडी स्पेस होता है. यह टारगेट रिसॉर्स आईडी स्पेस या अन्य ओवरले रिसॉर्स आईडी स्पेस से ओवरलैप नहीं होता. इसलिए, अपने रिसॉर्स का रेफ़रंस देने वाले ओवरले, उम्मीद के मुताबिक काम करते हैं.

  • Android 10 या इससे पहले के वर्शन में, ओवरले और टारगेट पैकेज एक ही रिसॉर्स आईडी स्पेस शेयर करते हैं. इससे टकराव हो सकता है और जब वे @type/name सिंटैक्स का इस्तेमाल करके अपने रिसॉर्स को रेफ़रंस करने की कोशिश करते हैं, तो गड़बड़ी हो सकती है.

ओवरले चालू/बंद करें

ओवरले को मैन्युअल तरीके से और प्रोग्राम के हिसाब से चालू/बंद किया जा सकता है.

ओवरले को मैन्युअल तरीके से बंद या चालू करना

किसी RRO को मैन्युअल तरीके से चालू करने और उसकी पुष्टि करने के लिए, यह कमांड चलाएं:

adb shell cmd overlay enable --user current com.example.carrro
adb shell cmd overlay list --user current | grep -i com.example com.example.carrro

इससे सिस्टम यूज़र (userId = 0) के लिए RRO चालू हो जाता है. यह सिस्टम यूज़र, SystemUI का मालिक होता है. इस निर्देश का असर, फ़ोरग्राउंड उपयोगकर्ता (userId = 10) की ओर से शुरू किए गए ऐप्लिकेशन पर नहीं पड़ता. फ़ोरग्राउंड उपयोगकर्ता के लिए RRO चालू करने के लिए, -–user 10 पैरामीटर का इस्तेमाल करें:

adb shell cmd overlay enable --user 10 com.example.carrro

प्रोग्राम के हिसाब से ओवरले चालू या बंद करना

बदले जा सकने वाले ओवरले को चालू और बंद करने के लिए, OverlayManager API का इस्तेमाल करें. Context#getSystemService(Context.OVERLAY_SERVICE) का इस्तेमाल करके, एपीआई इंटरफ़ेस को वापस पाएं. ओवरले को सिर्फ़ उस पैकेज से चालू किया जा सकता है जिसे वह टारगेट करता है या android.permission.CHANGE_OVERLAY_PACKAGES अनुमति वाले पैकेज से चालू किया जा सकता है. जब किसी ओवरले को चालू या बंद किया जाता है, तो कॉन्फ़िगरेशन में बदलाव करने वाले इवेंट, टारगेट पैकेज में फैल जाते हैं. साथ ही, टारगेट ऐक्टिविटी फिर से लॉन्च हो जाती हैं.

ओवरले किए जा सकने वाले संसाधनों को सीमित करना

Android 10 या इसके बाद के वर्शन में, <overlayable> एक्सएमएल टैग, संसाधनों का एक सेट दिखाता है. इन संसाधनों को RRO ओवरले कर सकते हैं. यहां दिए गए उदाहरण में, res/values/overlayable.xml फ़ाइल में string/foo और integer/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>

किसी APK में कई <overlayable> टैग तय किए जा सकते हैं. हालांकि, पैकेज में हर टैग का नाम यूनीक होना चाहिए. उदाहरण के लिए, यह है:

  • दो अलग-अलग पैकेज के लिए, <overlayable name="foo"> की जानकारी दी जा सकती है.

  • एक APK में दो <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> टैग में शामिल रिसॉर्स को ओवरले कर सकता है.

  • सिर्फ़ एक <overlayable> नाम को टारगेट किया जा सकता है.

ऐसे पैकेज को टारगेट करने वाले ओवरले को चालू नहीं किया जा सकता जो ओवरले किए जा सकने वाले संसाधन दिखाता है, लेकिन किसी खास <overlayable> टैग को टारगेट करने के लिए android:targetName का इस्तेमाल नहीं करता है.

नीतियों को सीमित करना

ओवरले किए जा सकने वाले संसाधनों पर पाबंदियां लागू करने के लिए, <policy> टैग का इस्तेमाल करें. type एट्रिब्यूट से यह पता चलता है कि शामिल किए गए संसाधनों को बदलने के लिए, किसी ओवरले को किन नीतियों का पालन करना होगा. ये टाइप इस्तेमाल किए जा सकते हैं.

  • public. कोई भी ओवरले, रिसॉर्स को बदल सकता है.
  • system. सिस्टम पार्टीशन पर मौजूद कोई भी ओवरले, संसाधनों को ओवरराइड कर सकता है.
  • vendor. वेंडर पार्टिशन पर मौजूद कोई भी ओवरले, संसाधनों को बदल सकता है.
  • product. प्रॉडक्ट पार्टीशन पर मौजूद कोई भी ओवरले, संसाधनों को ओवरराइड कर सकता है.
  • oem. ओईएम पार्टीशन पर मौजूद कोई भी ओवरले, संसाधनों को बदल सकता है.
  • odm. ओडीएम पार्टीशन पर मौजूद कोई भी ओवरले, संसाधनों को बदल सकता है.
  • signature. टारगेट APK पर जिस हस्ताक्षर का इस्तेमाल किया गया है उसी हस्ताक्षर का इस्तेमाल करके साइन किया गया कोई भी ओवरले, संसाधनों को बदल सकता है.
  • actor. ऐक्टर APK के तौर पर साइन किए गए किसी भी ओवरले में, संसाधनों को बदलने की सुविधा होती है. सिस्टम कॉन्फ़िगरेशन में, named-actor टैग में ऐक्टर का एलान किया गया है.
  • config_signature. overlay-config APK के तौर पर साइन किया गया कोई भी ओवरले, संसाधनों को बदल सकता है. सिस्टम कॉन्फ़िगरेशन में, overlay-config-signature टैग में overlay-config का एलान किया जाता है.

नीचे दिए गए कोड में, 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> टैग में शामिल संसाधनों को बदलने के लिए, ओवरले को सिर्फ़ एक नीति का पालन करना होगा.

ओवरले कॉन्फ़िगर करना

Android के अलग-अलग वर्शन के हिसाब से, ओवरले की स्थिति में बदलाव करने, डिफ़ॉल्ट स्थिति, और प्राथमिकता को कॉन्फ़िगर करने के लिए, Android अलग-अलग तरीकों का इस्तेमाल करता है.

  • Android 11 या इसके बाद के वर्शन वाले डिवाइसों पर, मेनिफ़ेस्ट एट्रिब्यूट के बजाय OverlayConfig फ़ाइल (config.xml) का इस्तेमाल किया जा सकता है. ओवरले के लिए, ओवरले फ़ाइल का इस्तेमाल करने का सुझाव दिया जाता है.

  • सभी डिवाइस, स्टैटिक RRO को कॉन्फ़िगर करने के लिए मेनिफ़ेस्ट एट्रिब्यूट (android:isStatic और android: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
  • odm
  • oem
  • 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:isStatic और android:priority का असर, पार्टीशन में मौजूद ओवरले पर नहीं पड़ता. किसी भी पार्टीशन में ओवरले कॉन्फ़िगरेशन फ़ाइल तय करने से, ओवरले पार्टीशन को प्राथमिकता मिलती है.

इसके अलावा, Android 11 या उसके बाद वाले वर्शन में, स्टैटिक ओवरले का इस्तेमाल करके पैकेज इंस्टॉल करने के दौरान पढ़ी गई वैल्यू में बदलाव करने की सुविधा हटा दी गई है. कॉम्पोनेंट के चालू होने की स्थिति को कॉन्फ़िगर करने वाले बूलियन की वैल्यू बदलने के लिए, स्टैटिक ओवरले का इस्तेमाल करने के सामान्य मामले में, <component-override> SystemConfig टैग का इस्तेमाल करें. यह टैग, Android 11 में नया है.

डीबग ओवरले

ओवरले को मैन्युअल तरीके से चालू, बंद, और डंप करने के लिए, ओवरले मैनेजर की इस शेल कमांड का इस्तेमाल करें.

adb shell cmd overlay

उपयोगकर्ता के बारे में जानकारी दिए बिना enable का इस्तेमाल करने से, मौजूदा उपयोगकर्ता पर असर पड़ता है. इसका मतलब है कि सिस्टम यूज़र (userId = 0), जिसके पास सिस्टम यूज़र इंटरफ़ेस (यूआई) का मालिकाना हक है. इससे, फ़ोरग्राउंड उपयोगकर्ता (userId = 10) पर कोई असर नहीं पड़ता. उसके पास ऐप्लिकेशन का मालिकाना हक होता है. स्क्रीन पर दिखने वाले उपयोगकर्ता के लिए, RRO चालू करने के लिए –-user 10 पैरामीटर का इस्तेमाल करें:

adb shell cmd overlay enable --user 10 com.example.carrro

OverlayManagerService, टारगेट पैकेज में मौजूद संसाधन आईडी को ओवरले पैकेज में मौजूद संसाधन आईडी से मैप करने के लिए idmap2 का इस्तेमाल करता है. जनरेट की गई आईडी मैपिंग, /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