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

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

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

ओवरले संसाधन

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

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

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

  • ज़रूरी android:targetPackage एट्रिब्यूट की वैल्यू से, उस पैकेज का नाम पता चलता है जिसे आरआरओ ओवरले करना चाहता है.

  • वैकल्पिक android:targetName एट्रिब्यूट की वैल्यू से, टारगेट पैकेज के उन संसाधनों के सबसेट का नाम पता चलता है जिन्हें आरआरओ ओवरले करना चाहता है. अगर टारगेट में संसाधनों का कोई ओवरले सेट नहीं बताया गया है, तो यह एट्रिब्यूट मौजूद नहीं होना चाहिए.

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

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

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

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

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

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

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

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

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

ओवरले किए जा सकने वाले रिसॉर्स पर पाबंदी लगाना

Android 10 या इसके बाद के वर्शन में, <overlayable> एक्सएमएल टैग से उन संसाधनों का एक सेट दिखता है जिन्हें आरआरओ ओवरले कर सकते हैं. यहां दिए गए उदाहरण में, 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. OEM पार्टीशन पर मौजूद कोई भी ओवरले, संसाधनों को बदल सकता है.
  • odm. ओडीएम पार्टीशन पर मौजूद कोई भी ओवरले, संसाधनों को बदल सकता है.
  • signature. टारगेट APK के हस्ताक्षर से मैच करने वाले हस्ताक्षर वाला कोई भी ओवरले, संसाधनों को बदल सकता है.
  • actor. ऐक्टर APK पर जिस हस्ताक्षर से साइन किया गया है उसी हस्ताक्षर से साइन किए गए किसी भी ओवरले से, संसाधनों को बदला जा सकता है. कलाकार की जानकारी, सिस्टम कॉन्फ़िगरेशन में named-actor टैग में दी जाती है.
  • config_signature. overlay-config 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> टैग में दिए गए संसाधनों को बदलने के लिए, ओवरले को सिर्फ़ एक नीति का पालन करना होगा.

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

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

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

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

मेनिफ़ेस्ट एट्रिब्यूट/स्टैटिक आरआरओ का इस्तेमाल करना

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 का, सेगमेंट में मौजूद ओवरले पर कोई असर नहीं पड़ता. किसी भी partition में ओवरले कॉन्फ़िगरेशन फ़ाइल तय करने पर, ओवरले partition को प्राथमिकता दी जाती है.

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

डीबग ओवरले

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

adb shell cmd overlay

किसी उपयोगकर्ता को बताए बिना enable का इस्तेमाल करने पर, मौजूदा उपयोगकर्ता पर असर पड़ता है. इसका मतलब है कि सिस्टम यूज़र (userId = 0) पर असर पड़ता है, जिसके पास सिस्टम यूज़र इंटरफ़ेस (यूआई) का मालिकाना हक होता है. इससे, फ़ोरग्राउंड में मौजूद उस उपयोगकर्ता (userId = 10) पर कोई असर नहीं पड़ता जिसके पास ऐप्लिकेशन का मालिकाना हक होता है. फ़ोरग्राउंड में मौजूद उपयोगकर्ता के लिए आरआरओ चालू करने के लिए, पैरामीटर –-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