स्थिर एआईडीएल

Android 10 में, Android इंटरफ़ेस डिफ़िनिशन लैंग्वेज (एआईडीएल) के लिए, काम करने वाले वर्शन का इस्तेमाल किया जा सकता है. यह एआईडीएल इंटरफ़ेस से मिलने वाले ऐप्लिकेशन प्रोग्रामिंग इंटरफ़ेस (एपीआई) और ऐप्लिकेशन बाइनरी इंटरफ़ेस (एबीआई) को ट्रैक करने का एक नया तरीका है. स्टेबल AIDL, AIDL की तरह ही काम करता है. हालांकि, बिल्ड सिस्टम इंटरफ़ेस के साथ काम करने की सुविधा को ट्रैक करता है. साथ ही, इस पर कुछ पाबंदियां भी हैं:

  • इंटरफ़ेस, बिल्ड सिस्टम में aidl_interfaces के साथ तय किए जाते हैं.
  • इंटरफ़ेस में सिर्फ़ स्ट्रक्चर्ड डेटा शामिल किया जा सकता है. पसंदीदा टाइप दिखाने वाले Parcelables, उनकी AIDL परिभाषा के आधार पर अपने-आप बन जाते हैं. साथ ही, ये अपने-आप मार्शल और अनमार्शल हो जाते हैं.
  • इंटरफ़ेस को स्थिर (पुराने सिस्टम के साथ काम करने की सुविधा वाला) के तौर पर घोषित किया जा सकता है. ऐसा होने पर, उनके एपीआई को ट्रैक किया जाता है और AIDL इंटरफ़ेस के बगल में मौजूद फ़ाइल में उसका वर्शन सेव किया जाता है.

स्ट्रक्चर्ड बनाम स्टैबल एआईडीएल

स्ट्रक्चर्ड AIDL का मतलब, पूरी तरह से AIDL में तय किए गए टाइप से है. उदाहरण के लिए, 'parcelsable' एलान (कस्टम parcelable) स्ट्रक्चर्ड एआईडीएल नहीं है. AIDL में तय किए गए फ़ील्ड वाले पार्सलबल को स्ट्रक्चर्ड पार्सलबल कहा जाता है.

स्टैबल एआईडीएल के लिए, स्ट्रक्चर्ड एआईडीएल की ज़रूरत होती है, ताकि बिल्ड सिस्टम और कंपाइलर यह समझ सकें कि पार्सल किए जा सकने वाले आइटम में किए गए बदलाव, पुराने सिस्टम के साथ काम करते हैं या नहीं. हालांकि, सभी स्ट्रक्चर्ड इंटरफ़ेस स्थिर नहीं होते. किसी इंटरफ़ेस को स्थिर रखने के लिए, ज़रूरी है कि वह सिर्फ़ स्ट्रक्चर्ड टाइप का इस्तेमाल करे. साथ ही, उसे वर्शनिंग की इन सुविधाओं का भी इस्तेमाल करना चाहिए. इसके उलट, अगर इंटरफ़ेस बनाने के लिए कोर बिल्ड सिस्टम का इस्तेमाल किया जाता है या unstable:true सेट किया जाता है, तो इंटरफ़ेस स्थिर नहीं होता.

AIDL इंटरफ़ेस तय करना

aidl_interface की परिभाषा इस तरह दिखती है:

aidl_interface {
    name: "my-aidl",
    srcs: ["srcs/aidl/**/*.aidl"],
    local_include_dir: "srcs/aidl",
    imports: ["other-aidl"],
    versions_with_info: [
        {
            version: "1",
            imports: ["other-aidl-V1"],
        },
        {
            version: "2",
            imports: ["other-aidl-V3"],
        }
    ],
    stability: "vintf",
    backend: {
        java: {
            enabled: true,
            platform_apis: true,
        },
        cpp: {
            enabled: true,
        },
        ndk: {
            enabled: true,
        },
        rust: {
            enabled: true,
        },
    },

}
  • name: AIDL इंटरफ़ेस मॉड्यूल का नाम, जो किसी AIDL इंटरफ़ेस की खास पहचान करता है.
  • srcs: इंटरफ़ेस बनाने वाली AIDL सोर्स फ़ाइलों की सूची. पैकेज com.acme में तय किए गए AIDL टाइप Foo का पाथ, <base_path>/com/acme/Foo.aidl पर होना चाहिए. यहां <base_path>, Android.bp वाली डायरेक्ट्री से जुड़ी कोई भी डायरेक्ट्री हो सकती है. पिछले उदाहरण में, <base_path> srcs/aidl है.
  • local_include_dir: वह पाथ जहां से पैकेज का नाम शुरू होता है. यह ऊपर बताए गए <base_path> से मेल खाता है.
  • imports: उन aidl_interface मॉड्यूल की सूची जिनका इस्तेमाल यह करता है. अगर आपका कोई एक एआईडीएल इंटरफ़ेस, किसी दूसरे aidl_interface के इंटरफ़ेस या पार्सल करने लायक ऑब्जेक्ट का इस्तेमाल करता है, तो उसका नाम यहां डालें. यह नाम, नए वर्शन का नाम हो सकता है. इसके अलावा, किसी खास वर्शन का नाम बताने के लिए, वर्शन के सफ़िक्स (जैसे, -V1) के साथ नाम भी हो सकता है. Android 12 से, वर्शन की जानकारी देने की सुविधा उपलब्ध है
  • versions: इंटरफ़ेस के पिछले वर्शन, जो api_dir में फ़्रीज़ किए गए हैं. Android 11 से, versions को aidl_api/name में फ़्रीज़ किया गया है. अगर किसी इंटरफ़ेस के फ़्रीज़ किए गए वर्शन मौजूद नहीं हैं, तो इसकी जानकारी नहीं दी जानी चाहिए. साथ ही, इसकी जांच भी नहीं की जाएगी कि यह वर्शन, पुराने सिस्टम के साथ काम करता है या नहीं. Android 13 और इसके बाद के वर्शन के लिए, इस फ़ील्ड को versions_with_info से बदल दिया गया है.
  • versions_with_info: ट्यूपल की सूची, जिसमें से हर ट्यूपल में, फ़्रीज़ किए गए वर्शन का नाम और उन अन्य aidl_interface मॉड्यूल के वर्शन इंपोर्ट की सूची होती है जिन्हें aidl_interface के इस वर्शन ने इंपोर्ट किया है. किसी AIDL इंटरफ़ेस IFACE के वर्शन V की परिभाषा, aidl_api/IFACE/V पर मौजूद है. इस फ़ील्ड को Android 13 में पेश किया गया था. साथ ही, इसे सीधे तौर पर Android.bp में बदलाव नहीं करना चाहिए. *-update-api या *-freeze-api को चालू करके, फ़ील्ड को जोड़ा या अपडेट किया जाता है. साथ ही, जब कोई उपयोगकर्ता *-update-api या *-freeze-api को चालू करता है, तो versions फ़ील्ड अपने-आप versions_with_info पर माइग्रेट हो जाते हैं.
  • stability: इस इंटरफ़ेस के स्थिर होने का वादा करने के लिए, ज़रूरी नहीं है. यह सिर्फ़ "vintf" के साथ काम करता है. अगर stability सेट नहीं है, तो बिल्ड सिस्टम यह जांच करता है कि इंटरफ़ेस, पुराने सिस्टम के साथ काम करता है या नहीं. ऐसा तब तक किया जाता है, जब तक unstable की वैल्यू तय नहीं की जाती. सेट न होने का मतलब है कि इस कंपाइलेशन कॉन्टेक्स्ट में, इंटरफ़ेस स्थिर है. उदाहरण के लिए, system.img और उससे जुड़े पार्टीशन में मौजूद सभी सिस्टम आइटम या vendor.img और उससे जुड़े पार्टीशन में मौजूद सभी वेंडर आइटम. अगर stability को "vintf" पर सेट किया गया है, तो इसका मतलब है कि इंटरफ़ेस को स्थिर रखने का वादा किया गया है: इसका इस्तेमाल होने तक, इंटरफ़ेस को स्थिर रखा जाना चाहिए.
  • gen_trace: ट्रैकिंग को चालू या बंद करने के लिए वैकल्पिक फ़्लैग. Android 14 से, cpp और java बैकएंड के लिए डिफ़ॉल्ट तौर पर true है.
  • host_supported: यह एक वैकल्पिक फ़्लैग है. इसे true पर सेट करने पर, जनरेट की गई लाइब्रेरी, होस्ट एनवायरमेंट के लिए उपलब्ध हो जाती हैं.
  • unstable: यह एक वैकल्पिक फ़्लैग है. इसका इस्तेमाल यह मार्क करने के लिए किया जाता है कि इस इंटरफ़ेस को स्थिर होने की ज़रूरत नहीं है. जब इसे true पर सेट किया जाता है, तो बिल्ड सिस्टम इंटरफ़ेस के लिए एपीआई डंप न तो बनाता है और न ही उसे अपडेट करता है.
  • frozen: यह एक ज़रूरी फ़्लैग नहीं है. इसे true पर सेट करने का मतलब है कि इंटरफ़ेस के पिछले वर्शन के बाद, इंटरफ़ेस में कोई बदलाव नहीं हुआ है. इससे, बिल्ड के समय ज़्यादा जांच की जा सकती है. false पर सेट होने का मतलब है कि इंटरफ़ेस पर काम चल रहा है और उसमें नए बदलाव किए गए हैं. इसलिए, foo-freeze-api को चलाने पर नया वर्शन जनरेट होता है और वैल्यू अपने-आप true पर सेट हो जाती है. इसे पहली बार Android 14 में जोड़ा गया था.
  • backend.<type>.enabled: ये फ़्लैग, उन सभी बैकएंड को टॉगल करते हैं जिनके लिए एआईडीएल कंपाइलर कोड जनरेट करता है. चार बैकएंड के साथ काम करता है: Java, C++, NDK, और Rust. Java, C++, और NDK बैकएंड डिफ़ॉल्ट रूप से चालू होते हैं. अगर इनमें से किसी बैकएंड की ज़रूरत नहीं है, तो उसे साफ़ तौर पर बंद करना होगा. Android 15 तक, Rust डिफ़ॉल्ट रूप से बंद रहता है.
  • backend.<type>.apex_available: उन APEX नामों की सूची जिनके लिए जनरेट की गई स्टब लाइब्रेरी उपलब्ध है.
  • backend.[cpp|java].gen_log: यह एक ज़रूरी फ़्लैग नहीं है. इससे यह तय होता है कि लेन-देन की जानकारी इकट्ठा करने के लिए, अतिरिक्त कोड जनरेट करना है या नहीं.
  • backend.[cpp|java].vndk.enabled: इस इंटरफ़ेस को VNDK का हिस्सा बनाने के लिए, वैकल्पिक फ़्लैग. डिफ़ॉल्ट रूप से false होता है.
  • backend.[cpp|ndk].additional_shared_libraries: Android 14 में जोड़ा गया यह फ़्लैग, नेटिव लाइब्रेरी में डिपेंडेंसी जोड़ता है. यह फ़्लैग, ndk_header और cpp_header के साथ काम आता है.
  • backend.java.sdk_version: SDK टूल के उस वर्शन की जानकारी देने के लिए वैकल्पिक फ़्लैग जिस पर Java स्टब लाइब्रेरी बनाई गई है. डिफ़ॉल्ट तौर पर, यह "system_current" पर सेट होता है. backend.java.platform_apis के true होने पर, इसे सेट नहीं किया जाना चाहिए.
  • backend.java.platform_apis: यह एक वैकल्पिक फ़्लैग है. जब जनरेट की गई लाइब्रेरी को SDK टूल के बजाय प्लैटफ़ॉर्म एपीआई के हिसाब से बनाना हो, तो इसे true पर सेट किया जाना चाहिए.

वर्शन और चालू किए गए बैकएंड के हर कॉम्बिनेशन के लिए, एक स्टब लाइब्रेरी बनाई जाती है. किसी खास बैकएंड के लिए, स्टब लाइब्रेरी के किसी खास वर्शन को रेफ़र करने का तरीका जानने के लिए, मॉड्यूल के नाम तय करने के नियम देखें.

एआईडीएल फ़ाइलें लिखना

स्टैबल एआईडीएल में मौजूद इंटरफ़ेस, सामान्य इंटरफ़ेस से मिलते-जुलते होते हैं. हालांकि, इनमें अनस्ट्रक्चर्ड पार्सलबल का इस्तेमाल नहीं किया जा सकता, क्योंकि ये स्टैबल नहीं होते! स्ट्रक्चर्ड बनाम स्टैबल एआईडीएल देखें. स्टेबल AIDL में मुख्य अंतर यह है कि 'parcels' को कैसे तय किया जाता है. पहले, पार्सल करने लायक आइटम को पहले से एलान किया जाता था. स्थिर (और इसलिए स्ट्रक्चर्ड) एआईडीएल में, पार्सल करने लायक आइटम के फ़ील्ड और वैरिएबल को साफ़ तौर पर बताया जाता है.

// in a file like 'some/package/Thing.aidl'
package some.package;

parcelable SubThing {
    String a = "foo";
    int b;
}

boolean, char, float, double, byte, int, long, और String के लिए डिफ़ॉल्ट वैल्यू का इस्तेमाल किया जा सकता है. हालांकि, ऐसा करना ज़रूरी नहीं है. Android 12 में, उपयोगकर्ता की ओर से तय किए गए एन्यूमरेशन के लिए डिफ़ॉल्ट वैल्यू भी इस्तेमाल की जा सकती हैं. अगर कोई डिफ़ॉल्ट वैल्यू नहीं दी गई है, तो 0 जैसी या खाली वैल्यू का इस्तेमाल किया जाता है. जिन एनोटेशन के लिए कोई डिफ़ॉल्ट वैल्यू नहीं दी गई है उन्हें 0 पर शुरू किया जाता है. भले ही, उनमें कोई 'शून्य' एनोरेटर न हो.

स्टब लाइब्रेरी का इस्तेमाल करना

अपने मॉड्यूल में स्टब लाइब्रेरी को डिपेंडेंसी के तौर पर जोड़ने के बाद, उन्हें अपनी फ़ाइलों में शामिल किया जा सकता है. यहां बिल्ड सिस्टम में स्टब लाइब्रेरी के उदाहरण दिए गए हैं (Android.mk का इस्तेमाल, लेगसी मॉड्यूल की परिभाषाओं के लिए भी किया जा सकता है). ध्यान दें, इन उदाहरणों में वर्शन मौजूद नहीं है. इसलिए, यह अस्थिर इंटरफ़ेस का इस्तेमाल करने के बारे में बताता है. हालांकि, वर्शन वाले इंटरफ़ेस के नाम में ज़्यादा जानकारी शामिल होती है. इंटरफ़ेस के वर्शन देखें.

cc_... {
    name: ...,
    // use `shared_libs:` to load your library and its transitive dependencies
    // dynamically
    shared_libs: ["my-module-name-cpp"],
    // use `static_libs:` to include the library in this binary and drop
    // transitive dependencies
    static_libs: ["my-module-name-cpp"],
    ...
}
# or
java_... {
    name: ...,
    // use `static_libs:` to add all jars and classes to this jar
    static_libs: ["my-module-name-java"],
    // use `libs:` to make these classes available during build time, but
    // not add them to the jar, in case the classes are already present on the
    // boot classpath (such as if it's in framework.jar) or another jar.
    libs: ["my-module-name-java"],
    // use `srcs:` with `-java-sources` if you want to add classes in this
    // library jar directly, but you get transitive dependencies from
    // somewhere else, such as the boot classpath or another jar.
    srcs: ["my-module-name-java-source", ...],
    ...
}
# or
rust_... {
    name: ...,
    rustlibs: ["my-module-name-rust"],
    ...
}

C++ में उदाहरण:

#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
    // use just like traditional AIDL

Java में उदाहरण:

import some.package.IFoo;
import some.package.Thing;
...
    // use just like traditional AIDL

Rust में उदाहरण:

use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
    // use just like traditional AIDL

इंटरफ़ेस के वर्शन

foo नाम से मॉड्यूल का एलान करने पर, बिल्ड सिस्टम में एक टारगेट भी बन जाता है. इसका इस्तेमाल, मॉड्यूल के एपीआई को मैनेज करने के लिए किया जा सकता है. बिल्ड होने पर, foo-freeze-api, Android वर्शन के आधार पर api_dir या aidl_api/name में एक नई एपीआई परिभाषा जोड़ता है. साथ ही, एक .hash फ़ाइल जोड़ता है. ये दोनों इंटरफ़ेस के नए फ़्रीज़ किए गए वर्शन को दिखाते हैं. foo-freeze-api, वर्शन के लिए अतिरिक्त वर्शन और imports को दिखाने के लिए, versions_with_info प्रॉपर्टी को भी अपडेट करता है. आम तौर पर, versions_with_info में मौजूद imports को imports फ़ील्ड से कॉपी किया जाता है. हालांकि, इंपोर्ट के लिए versions_with_info में imports में, सबसे नया स्टेबल वर्शन बताया गया है, जिसमें कोई वर्शन नहीं है. versions_with_info प्रॉपर्टी तय करने के बाद, बिल्ड सिस्टम, फ़्रीज़ किए गए वर्शन के बीच और टॉप ऑफ़ ट्री (ToT) और सबसे नए फ़्रीज़ किए गए वर्शन के बीच, काम करने की जांच करता है.

इसके अलावा, आपको ToT वर्शन के एपीआई की परिभाषा मैनेज करनी होगी. जब भी कोई एपीआई अपडेट किया जाता है, तो aidl_api/name/current को अपडेट करने के लिए foo-update-api चलाएं. इसमें ToT वर्शन का एपीआई डेफ़िनिशन होता है.

इंटरफ़ेस को बेहतर बनाने के लिए, मालिक ये नए आइटम जोड़ सकते हैं:

  • इंटरफ़ेस के आखिर में मौजूद तरीके (या साफ़ तौर पर बताए गए नए तरीकों के साथ)
  • पार्सल करने लायक ऑब्जेक्ट के आखिर में मौजूद एलिमेंट (हर एलिमेंट के लिए डिफ़ॉल्ट वैल्यू जोड़ना ज़रूरी है)
  • कॉन्स्टेंट वैल्यू
  • Android 11 में, एनोमेटर
  • Android 12 में, यूनियन के आखिर में मौजूद फ़ील्ड

कोई और कार्रवाई करने की अनुमति नहीं है. साथ ही, कोई दूसरा व्यक्ति इंटरफ़ेस में बदलाव नहीं कर सकता. ऐसा करने पर, मालिक के किए गए बदलावों में गड़बड़ी हो सकती है.

यह जांचने के लिए कि रिलीज़ के लिए सभी इंटरफ़ेस फ़्रीज़ किए गए हैं या नहीं, इन एनवायरमेंट वैरिएबल के सेट के साथ बिल्ड किया जा सकता है:

  • AIDL_FROZEN_REL=true m ... - बिल्ड के लिए, उन सभी स्थिर एआईडीएल इंटरफ़ेस को फ़्रीज़ करना ज़रूरी है जिनमें कोई owner: फ़ील्ड नहीं है.
  • AIDL_FROZEN_OWNERS="aosp test" - बिल्ड के लिए, सभी स्थिर AIDL इंटरफ़ेस को owner: फ़ील्ड के साथ फ़्रीज़ करना ज़रूरी है. इस फ़ील्ड को "aosp" या "test" के तौर पर सेट किया जाना चाहिए.

इंपोर्ट की स्थिरता

किसी इंटरफ़ेस के फ़्रीज़ किए गए वर्शन के लिए, इंपोर्ट के वर्शन अपडेट करने पर, स्टेबल AIDL लेयर पर बैकवर्ड कम्पैटिबल होता है. हालांकि, इन्हें अपडेट करने के लिए, इंटरफ़ेस के पिछले वर्शन का इस्तेमाल करने वाले सभी सर्वर और क्लाइंट को अपडेट करना ज़रूरी है. साथ ही, अलग-अलग टाइप के वर्शन को मिलाने पर, कुछ ऐप्लिकेशन में गड़बड़ी हो सकती है. आम तौर पर, सिर्फ़ टाइप या सामान्य पैकेज के लिए, यह सुरक्षित है, क्योंकि आईपीसी लेन-देन से अनजान टाइप को मैनेज करने के लिए, कोड को पहले से लिखा जाना चाहिए.

Android प्लैटफ़ॉर्म कोड में, android.hardware.graphics.common इस तरह के वर्शन अपग्रेड का सबसे बड़ा उदाहरण है.

वर्शन वाले इंटरफ़ेस का इस्तेमाल करना

इंटरफ़ेस के तरीके

रनटाइम के दौरान, किसी पुराने सर्वर पर नए तरीके कॉल करने पर, नए क्लाइंट को बैकएंड के आधार पर गड़बड़ी या अपवाद मिलता है.

  • cpp बैकएंड को ::android::UNKNOWN_TRANSACTION मिलता है.
  • ndk बैकएंड को STATUS_UNKNOWN_TRANSACTION मिलता है.
  • java बैकएंड को android.os.RemoteException कोड मिलता है. साथ ही, एक मैसेज मिलता है जिसमें लिखा होता है कि एपीआई लागू नहीं किया गया है.

इस समस्या को हल करने की रणनीतियों के लिए, वर्शन के बारे में क्वेरी करना और डिफ़ॉल्ट का इस्तेमाल करना लेख पढ़ें.

पार्सल किए जा सकने वाले आइटम

जब पार्सल करने लायक ऑब्जेक्ट में नए फ़ील्ड जोड़े जाते हैं, तो पुराने क्लाइंट और सर्वर उन्हें हटा देते हैं. जब नए क्लाइंट और सर्वर को पुराने पार्सलबल मिलते हैं, तो नए फ़ील्ड के लिए डिफ़ॉल्ट वैल्यू अपने-आप भर जाती हैं. इसका मतलब है कि पार्सल किए जा सकने वाले किसी आइटम में, सभी नए फ़ील्ड के लिए डिफ़ॉल्ट वैल्यू तय करनी होंगी.

क्लाइंट को तब तक नए फ़ील्ड का इस्तेमाल करने की उम्मीद नहीं करनी चाहिए, जब तक उन्हें यह पता न हो कि सर्वर उस वर्शन को लागू कर रहा है जिसमें फ़ील्ड तय किया गया है (वर्शन के बारे में क्वेरी करना देखें).

Enums और कॉन्स्टेंट

इसी तरह, क्लाइंट और सर्वर को ऐसी वैल्यू और एनोमेरेटर को अस्वीकार करना चाहिए जिन्हें पहचाना नहीं जा सका है. ऐसा तब करना चाहिए, जब वे वैल्यू और एनोमेरेटर काम के न हों. ऐसा इसलिए, क्योंकि आने वाले समय में और भी वैल्यू और एनोमेरेटर जोड़े जा सकते हैं. उदाहरण के लिए, जब किसी सर्वर को ऐसा एनोमेरेटर मिलता है जिसके बारे में उसे पता नहीं है, तो उसे प्रोसेस को बंद नहीं करना चाहिए. सर्वर को एनोमेरेटर को अनदेखा करना चाहिए या कुछ ऐसा दिखाना चाहिए जिससे क्लाइंट को पता चल सके कि इस तरीके से लागू करने पर, एनोमेरेटर काम नहीं करता.

यूनियन

अगर रिसीवर पुराना है और उसे फ़ील्ड के बारे में जानकारी नहीं है, तो नए फ़ील्ड के साथ यूनियन भेजने की कोशिश करने पर, गड़बड़ी का मैसेज मिलता है. लागू करने पर, नए फ़ील्ड के साथ यूनियन कभी नहीं दिखेगा. अगर लेन-देन एकतरफा है, तो गड़बड़ी को अनदेखा कर दिया जाता है. अगर ऐसा नहीं है, तो गड़बड़ी BAD_VALUE(C++ या NDK बैकएंड के लिए) या IllegalArgumentException(Java बैकएंड के लिए) होती है. यह गड़बड़ी तब मिलती है, जब क्लाइंट नए फ़ील्ड में यूनियन सेट को किसी पुराने सर्वर पर भेज रहा हो या जब कोई पुराना क्लाइंट, नए सर्वर से यूनियन पा रहा हो.

एक से ज़्यादा वर्शन मैनेज करना

Android के लिंकर नेमस्पेस में, किसी खास aidl इंटरफ़ेस का सिर्फ़ एक वर्शन हो सकता है. इससे, जनरेट किए गए aidl टाइप की कई परिभाषाओं से जुड़ी समस्याओं से बचा जा सकता है. C++ में एक परिभाषा वाला नियम है, जिसके तहत हर सिंबल के लिए सिर्फ़ एक परिभाषा की ज़रूरत होती है.

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

अगर इंटरफ़ेस लाइब्रेरी का इस्तेमाल कई अलग-अलग मॉड्यूल करते हैं, तो लाइब्रेरी और प्रोसेस के किसी भी ग्रुप के लिए cc_defaults, java_defaults, और rust_defaults बनाना मददगार हो सकता है. ऐसा तब करना चाहिए, जब लाइब्रेरी और प्रोसेस के सभी ग्रुप को एक ही वर्शन का इस्तेमाल करना हो. इंटरफ़ेस के नए वर्शन को लॉन्च करते समय, डिफ़ॉल्ट वैल्यू को अपडेट किया जा सकता है. साथ ही, उनका इस्तेमाल करने वाले सभी मॉड्यूल को एक साथ अपडेट किया जा सकता है. इससे यह पक्का किया जा सकता है कि वे इंटरफ़ेस के अलग-अलग वर्शन का इस्तेमाल न कर रहे हों.

cc_defaults {
  name: "my.aidl.my-process-group-ndk-shared",
  shared_libs: ["my.aidl-V3-ndk"],
  ...
}

cc_library {
  name: "foo",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

cc_binary {
  name: "bar",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

जब aidl_interface मॉड्यूल, दूसरे aidl_interface मॉड्यूल इंपोर्ट करते हैं, तो इससे अतिरिक्त डिपेंडेंसी बनती हैं. इन डिपेंडेंसी के लिए, एक साथ कुछ खास वर्शन का इस्तेमाल करना ज़रूरी होता है. अगर एक ही प्रोसेस में एक से ज़्यादा aidl_interface मॉड्यूल का इस्तेमाल किया जाता है और उनमें एक जैसे aidl_interface मॉड्यूल इंपोर्ट किए जाते हैं, तो इस स्थिति को मैनेज करना मुश्किल हो सकता है.

aidl_interfaces_defaults का इस्तेमाल, किसी aidl_interface के लिए डिपेंडेंसी के नए वर्शन की एक परिभाषा को एक ही जगह पर अपडेट करने के लिए किया जा सकता है. साथ ही, उस सामान्य इंटरफ़ेस को इंपोर्ट करने वाले सभी aidl_interface मॉड्यूल इसका इस्तेमाल कर सकते हैं.

aidl_interface_defaults {
  name: "android.popular.common-latest-defaults",
  imports: ["android.popular.common-V3"],
  ...
}

aidl_interface {
  name: "android.foo",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

aidl_interface {
  name: "android.bar",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

शिकायत के आधार पर डेवलपमेंट

जिन इंटरफ़ेस पर काम चल रहा है (जिन्हें फ़्रीज़ नहीं किया गया है) उनका इस्तेमाल रिलीज़ किए गए डिवाइसों पर नहीं किया जा सकता. ऐसा इसलिए, क्योंकि यह पक्का नहीं है कि वे पुराने वर्शन के साथ काम करेंगे.

AIDL, अनफ़्रीज़ की गई इन इंटरफ़ेस लाइब्रेरी के लिए, रन टाइम फ़ॉलबैक की सुविधा देता है. इससे, अनफ़्रीज़ किए गए नए वर्शन के लिए कोड लिखा जा सकता है और उसे रिलीज़ किए गए डिवाइसों पर इस्तेमाल किया जा सकता है. क्लाइंट का बैकवर्ड-कंपैटिबल व्यवहार, मौजूदा व्यवहार से मिलता-जुलता है. साथ ही, फ़ॉलबैक के साथ, लागू करने के लिए भी उन व्यवहारों का पालन करना ज़रूरी है. अलग-अलग वर्शन वाले इंटरफ़ेस का इस्तेमाल करना देखें.

एआईडीएल बिल्ड फ़्लैग

इस व्यवहार को कंट्रोल करने वाले फ़्लैग के बारे में build/release/build_flags.bzl में बताया गया है.RELEASE_AIDL_USE_UNFROZEN true का मतलब है कि रन टाइम के दौरान, इंटरफ़ेस के अनफ़्रीज़ किए गए वर्शन का इस्तेमाल किया जाता है. वहीं, false का मतलब है कि अनफ़्रीज़ किए गए वर्शन की लाइब्रेरी, अपने आखिरी फ़्रीज़ किए गए वर्शन की तरह काम करती हैं. स्थानीय डेवलपमेंट के लिए, फ़्लैग को true पर बदला जा सकता है. हालांकि, रिलीज़ करने से पहले, इसे false पर वापस ले जाना होगा. आम तौर पर, डेवलपमेंट उस कॉन्फ़िगरेशन के साथ किया जाता है जिसमें फ़्लैग को true पर सेट किया गया हो.

साथ काम करने वाले वर्शन की मैट्रिक और मेनिफ़ेस्ट

वेंडर इंटरफ़ेस ऑब्जेक्ट (VINTF ऑब्जेक्ट) से यह तय होता है कि कौनसे वर्शन इस्तेमाल किए जा सकते हैं और वेंडर इंटरफ़ेस के दोनों तरफ़ कौनसे वर्शन उपलब्ध कराए गए हैं.

Cuttlefish के अलावा, ज़्यादातर डिवाइस इंटरफ़ेस फ़्रीज़ होने के बाद ही, काम करने के लिए डिवाइसों के बीच के नए मैट्रिक को टारगेट करते हैं. इसलिए, RELEASE_AIDL_USE_UNFROZEN के आधार पर AIDL लाइब्रेरी में कोई अंतर नहीं होता.

मैट्रिक

पार्टनर के मालिकाना हक वाले इंटरफ़ेस, डिवाइस या प्रॉडक्ट के हिसाब से काम करने वाले मैट्रिक में जोड़े जाते हैं. इन मैट्रिक को डेवलपमेंट के दौरान डिवाइस टारगेट करता है. इसलिए, जब किसी इंटरफ़ेस का नया और अनफ़्रीज़ किया गया वर्शन, काम करने के साथ-साथ वर्शन की जानकारी देने वाले मैट्रिक में जोड़ा जाता है, तो RELEASE_AIDL_USE_UNFROZEN=false के लिए, पिछले फ़्रीज़ किए गए वर्शन मौजूद होने चाहिए. अलग-अलग RELEASE_AIDL_USE_UNFROZEN कॉन्फ़िगरेशन के लिए, अलग-अलग कंपैटिबिलिटी मैट्रिक्स फ़ाइलों का इस्तेमाल करके या सभी कॉन्फ़िगरेशन में इस्तेमाल की जाने वाली एक कंपैटिबिलिटी मैट्रिक्स फ़ाइल में, दोनों वर्शन को अनुमति देकर इसे मैनेज किया जा सकता है.

उदाहरण के लिए, अनफ़्रीज़ किया गया वर्शन 4 जोड़ते समय, <version>3-4</version> का इस्तेमाल करें.

जब वर्शन 4 को फ़्रीज़ कर दिया जाता है, तो वर्शन 3 को काम करने के साथ-साथ, वर्शन के मैट्रिक से हटाया जा सकता है. ऐसा इसलिए किया जा सकता है, क्योंकि फ़्रीज़ किए गए वर्शन 4 का इस्तेमाल तब किया जाता है, जब RELEASE_AIDL_USE_UNFROZEN false हो.

मेनिफ़ेस्ट

Android 15 में, libvintf में बदलाव किया गया है. इससे, libvintf की वैल्यू के आधार पर, बिल्ड के समय मेनिफ़ेस्ट फ़ाइलों में बदलाव किया जा सकता है.RELEASE_AIDL_USE_UNFROZEN

मेनिफ़ेस्ट और मेनिफ़ेस्ट फ़्रैगमेंट से पता चलता है कि कोई सेवा, इंटरफ़ेस का कौनसा वर्शन लागू करती है. किसी इंटरफ़ेस के अनफ़्रीज़ किए गए नए वर्शन का इस्तेमाल करते समय, इस नए वर्शन को दिखाने के लिए मेनिफ़ेस्ट को अपडेट करना ज़रूरी है. जनरेट की गई एआईडीएल लाइब्रेरी में हुए बदलाव को दिखाने के लिए, RELEASE_AIDL_USE_UNFROZEN=false के ज़रिए मेनिफ़ेस्ट एंट्री में बदलाव किया जाता है.libvintf वर्शन को, फ़्रीज़ किए गए वर्शन N से बदलकर, आखिरी फ़्रीज़ किए गए वर्शन N - 1 पर सेट किया गया है. इसलिए, उपयोगकर्ताओं को अपनी हर सेवा के लिए, एक से ज़्यादा मेनिफ़ेस्ट या मेनिफ़ेस्ट फ़्रैगमेंट मैनेज करने की ज़रूरत नहीं होती.

HAL क्लाइंट में हुए बदलाव

HAL क्लाइंट कोड, काम करने वाले हर पुराने फ़्रीज़ किए गए वर्शन के साथ काम करना चाहिए. जब RELEASE_AIDL_USE_UNFROZEN की वैल्यू false होती है, तो सेवाएं हमेशा फ़्रीज़ किए गए पिछले वर्शन या उससे पहले के वर्शन जैसी दिखती हैं. उदाहरण के लिए, फ़्रीज़ नहीं किए गए नए तरीकों को कॉल करने पर UNKNOWN_TRANSACTION दिखता है या नए parcelable फ़ील्ड में डिफ़ॉल्ट वैल्यू दिखती हैं. Android फ़्रेमवर्क क्लाइंट, पिछले वर्शन के साथ काम करने चाहिए. हालांकि, यह जानकारी पार्टनर के मालिकाना हक वाले इंटरफ़ेस के वेंडर क्लाइंट और क्लाइंट के लिए नई है.

एचएएल लागू करने से जुड़े बदलाव

फ़्लैग-आधारित डेवलपमेंट के मुकाबले, एचएएल डेवलपमेंट में सबसे बड़ा फ़र्क़ यह है कि एचएएल लागू करने के लिए, यह ज़रूरी है कि वह आखिरी फ़्रीज़ किए गए वर्शन के साथ बैकवर्ड कम्पैटिबल हो, ताकि RELEASE_AIDL_USE_UNFROZEN के false होने पर वह काम कर सके. लागू करने और डिवाइस कोड में पिछली सुविधाओं के साथ काम करने की सुविधा को ध्यान में रखना एक नया तरीका है. वर्शन वाले इंटरफ़ेस का इस्तेमाल करना लेख पढ़ें.

आम तौर पर, बैकवर्ड कम्पैटिबिलिटी से जुड़ी बातें क्लाइंट और सर्वर के लिए एक जैसी होती हैं. साथ ही, फ़्रेमवर्क कोड और वेंडर कोड के लिए भी एक जैसी होती हैं. हालांकि, इनमें कुछ छोटे अंतर होते हैं, जिनके बारे में आपको पता होना चाहिए. ऐसा इसलिए है, क्योंकि अब एक ही सोर्स कोड (मौजूदा, अनफ़्रीज़ किया गया वर्शन) का इस्तेमाल करने वाले दो वर्शन को असरदार तरीके से लागू किया जा रहा है.

उदाहरण: किसी इंटरफ़ेस के तीन फ़्रीज़ किए गए वर्शन हैं. इंटरफ़ेस को नए तरीके से अपडेट किया गया है. क्लाइंट और सेवा, दोनों को लाइब्रेरी के नए वर्शन 4 का इस्तेमाल करने के लिए अपडेट किया गया है. V4 लाइब्रेरी, इंटरफ़ेस के अनफ़्रीज़ किए गए वर्शन पर आधारित है. इसलिए, RELEASE_AIDL_USE_UNFROZEN के false होने पर, यह आखिरी फ़्रीज़ किए गए वर्शन, वर्शन 3 की तरह काम करती है. साथ ही, नए तरीके का इस्तेमाल करने से रोकती है.

इंटरफ़ेस फ़्रीज़ होने पर, RELEASE_AIDL_USE_UNFROZEN की सभी वैल्यू उस फ़्रीज़ किए गए वर्शन का इस्तेमाल करती हैं. साथ ही, पुराने वर्शन के साथ काम करने वाले कोड को हटाया जा सकता है.

कॉलबैक पर तरीके कॉल करते समय, आपको UNKNOWN_TRANSACTION के रिटर्न होने पर, उस स्थिति को सही तरीके से मैनेज करना होगा. रिलीज़ कॉन्फ़िगरेशन के आधार पर, क्लाइंट किसी कॉलबैक के दो अलग-अलग वर्शन लागू कर सकते हैं. इसलिए, यह नहीं माना जा सकता कि क्लाइंट सबसे नया वर्शन भेजता है और नए तरीके इसे दिखा सकते हैं. यह उसी तरह है जिस तरह वर्शन वाले इंटरफ़ेस का इस्तेमाल करें में बताया गया है कि स्टैबल AIDL क्लाइंट, सर्वर के साथ बैकवर्ड कम्पैटिबिलिटी बनाए रखते हैं.

// Get the callback along with the version of the callback
ScopedAStatus RegisterMyCallback(const std::shared_ptr<IMyCallback>& cb) override {
    mMyCallback = cb;
    // Get the version of the callback for later when we call methods on it
    auto status = mMyCallback->getInterfaceVersion(&mMyCallbackVersion);
    return status;
}

// Example of using the callback later
void NotifyCallbackLater() {
  // From the latest frozen version (V2)
  mMyCallback->foo();
  // Call this method from the unfrozen V3 only if the callback is at least V3
  if (mMyCallbackVersion >= 3) {
    mMyCallback->bar();
  }
}

हो सकता है कि मौजूदा टाइप (parcelable, enum, union) में नए फ़ील्ड मौजूद न हों या RELEASE_AIDL_USE_UNFROZEN के false होने पर, उनमें डिफ़ॉल्ट वैल्यू मौजूद न हों. साथ ही, सेवा की ओर से भेजे जाने वाले नए फ़ील्ड की वैल्यू, प्रोसेस के दौरान हटा दी जाती हैं.

अनफ़्रीज़ किए गए इस वर्शन में जोड़े गए नए टाइप, इंटरफ़ेस के ज़रिए भेजे या पाए नहीं जा सकते.

जब RELEASE_AIDL_USE_UNFROZEN false हो, तो लागू करने वाले को किसी भी क्लाइंट से नए तरीकों के लिए कॉल कभी नहीं मिलता.

नए एनोमेरेटर का इस्तेमाल, सिर्फ़ उस वर्शन के साथ करें जिसमें उन्हें लॉन्च किया गया है, न कि पुराने वर्शन के साथ.

आम तौर पर, foo->getInterfaceVersion() का इस्तेमाल करके यह देखा जाता है कि रिमोट इंटरफ़ेस किस वर्शन का इस्तेमाल कर रहा है. हालांकि, फ़्लैग के आधार पर वर्शन बनाने की सुविधा का इस्तेमाल करके, दो अलग-अलग वर्शन लागू किए जा रहे हैं. इसलिए, हो सकता है कि आपको मौजूदा इंटरफ़ेस का वर्शन चाहिए हो. ऐसा करने के लिए, मौजूदा ऑब्जेक्ट का इंटरफ़ेस वर्शन पाएं. उदाहरण के लिए, this->getInterfaceVersion() या my_ver के लिए अन्य तरीके. ज़्यादा जानकारी के लिए, रिमोट ऑब्जेक्ट के इंटरफ़ेस वर्शन के बारे में क्वेरी करना देखें.

VINTF के नए और बेहतर इंटरफ़ेस

जब कोई नया AIDL इंटरफ़ेस पैकेज जोड़ा जाता है, तो पिछला फ़्रीज़ किया गया वर्शन नहीं होता. इसलिए, RELEASE_AIDL_USE_UNFROZEN के false होने पर, कोई बैकफ़ॉल व्यवहार नहीं होता. इन इंटरफ़ेस का इस्तेमाल न करें. जब RELEASE_AIDL_USE_UNFROZEN का वैल्यू false है, तो Service Manager, इंटरफ़ेस को रजिस्टर करने की अनुमति सेवा को नहीं देगा. साथ ही, क्लाइंट को वह इंटरफ़ेस नहीं दिखेगा.

डिवाइस की मेकफ़ाइल में RELEASE_AIDL_USE_UNFROZEN फ़्लैग की वैल्यू के आधार पर, सेवाओं को शर्त के साथ जोड़ा जा सकता है:

ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
    android.hardware.health.storage-service
endif

अगर सेवा किसी बड़ी प्रोसेस का हिस्सा है, तो उसे डिवाइस में शर्त के साथ नहीं जोड़ा जा सकता. ऐसे में, यह देखा जा सकता है कि सेवा को IServiceManager::isDeclared() के साथ एलान किया गया है या नहीं. अगर यह एलान किया गया है और रजिस्ट्रेशन नहीं हो पाया है, तो प्रोसेस को बंद कर दें. अगर इसे एलान नहीं किया जाता है, तो हो सकता है कि इसे रजिस्टर न किया जा सके.

डेवलपमेंट टूल के तौर पर Cuttlefish

VINTF फ़्रीज़ होने के बाद, हम हर साल फ़्रेमवर्क के साथ काम करने वाले डिवाइसों के मैट्रिक (FCM) target-level और Cuttlefish के PRODUCT_SHIPPING_API_LEVEL में बदलाव करते हैं, ताकि अगले साल रिलीज़ होने वाले डिवाइसों की जानकारी दिखे. हम target-level और PRODUCT_SHIPPING_API_LEVEL में बदलाव करते हैं, ताकि यह पक्का किया जा सके कि लॉन्च होने वाले डिवाइस की जांच की गई हो और वह अगले साल रिलीज़ होने वाले वर्शन की नई ज़रूरी शर्तों को पूरा करता हो.

जब RELEASE_AIDL_USE_UNFROZEN true होता है, तो Cuttlefish का इस्तेमाल, आने वाले समय में रिलीज़ होने वाले Android वर्शन को डेवलप करने के लिए किया जाता है. यह अगले साल के Android रिलीज़ के FCM लेवल और PRODUCT_SHIPPING_API_LEVEL को टारगेट करता है. इसके लिए, इसे अगले रिलीज़ के वेंडर सॉफ़्टवेयर की ज़रूरी शर्तों (वीएसआर) को पूरा करना होगा.

जब RELEASE_AIDL_USE_UNFROZEN false है, तो रिलीज़ डिवाइस दिखाने के लिए, Cuttlefish में पिछला target-level और PRODUCT_SHIPPING_API_LEVEL होता है. Android 14 और उससे पहले के वर्शन में, यह फ़र्क़ अलग-अलग Git ब्रैंच की मदद से किया जाएगा. ये ब्रैंच, FCM target-level, शिपिंग एपीआई लेवल या अगली रिलीज़ को टारगेट करने वाले किसी भी दूसरे कोड में हुए बदलाव को शामिल नहीं करते.

मॉड्यूल का नाम रखने के नियम

Android 11 में, वर्शन और चालू किए गए बैकएंड के हर कॉम्बिनेशन के लिए, स्टब लाइब्रेरी मॉड्यूल अपने-आप बन जाता है. लिंक करने के लिए किसी खास स्टब लाइब्रेरी मॉड्यूल का रेफ़रंस देने के लिए, aidl_interface मॉड्यूल के नाम का इस्तेमाल न करें. इसके बजाय, स्टब लाइब्रेरी मॉड्यूल का नाम इस्तेमाल करें, जो कि ifacename-version-backend है.

  • ifacename: aidl_interface मॉड्यूल का नाम
  • version, इनमें से कोई एक है
    • फ़्रीज़ किए गए वर्शन के लिए Vversion-number
    • Vlatest-frozen-version-number + 1, ट्री के सबसे ऊपर मौजूद (अभी तक फ़्रीज़ नहीं किया गया) वर्शन के लिए
  • backend, इनमें से कोई एक है
    • java Java बैकएंड के लिए,
    • C++ बैकएंड के लिए cpp,
    • NDK बैकएंड के लिए ndk या ndk_platform. पहला, ऐप्लिकेशन के लिए है और दूसरा, Android 13 तक प्लैटफ़ॉर्म के इस्तेमाल के लिए है. Android 13 और उसके बाद के वर्शन में, सिर्फ़ ndk का इस्तेमाल करें.
    • Rust बैकएंड के लिए rust.

मान लें कि foo नाम का एक मॉड्यूल है और उसका नया वर्शन 2 है. साथ ही, यह NDK और C++, दोनों के साथ काम करता है. इस मामले में, AIDL ये मॉड्यूल जनरेट करता है:

  • वर्शन 1 के आधार पर
    • foo-V1-(java|cpp|ndk|ndk_platform|rust)
  • वर्शन 2 (सबसे नया स्टेबल वर्शन) के आधार पर
    • foo-V2-(java|cpp|ndk|ndk_platform|rust)
  • ToT वर्शन के आधार पर
    • foo-V3-(java|cpp|ndk|ndk_platform|rust)

Android 11 की तुलना में:

  • foo-backend, जो सबसे नए स्थिर वर्शन का रेफ़रंस देता है वह foo-V2-backend हो जाता है
  • foo-unstable-backend, जो ToT वर्शन को रेफ़र करता है वह foo-V3-backend हो जाता है

आउटपुट फ़ाइलों के नाम हमेशा मॉड्यूल के नामों से मेल खाते हैं.

  • पहले वर्शन के आधार पर: foo-V1-(cpp|ndk|ndk_platform|rust).so
  • दूसरे वर्शन के आधार पर: foo-V2-(cpp|ndk|ndk_platform|rust).so
  • ToT वर्शन के आधार पर: foo-V3-(cpp|ndk|ndk_platform|rust).so

ध्यान दें कि AIDL कंपाइलर, स्टैबल AIDL इंटरफ़ेस के लिए, unstable वर्शन मॉड्यूल या बिना वर्शन वाला मॉड्यूल नहीं बनाता. Android 12 के बाद, स्थिर AIDL इंटरफ़ेस से जनरेट किए गए मॉड्यूल के नाम में, हमेशा उसका वर्शन शामिल होता है.

मेटा इंटरफ़ेस के नए तरीके

Android 10 में, स्थिर AIDL के लिए कई मेटा इंटरफ़ेस तरीके जोड़े गए हैं.

रिमोट ऑब्जेक्ट के इंटरफ़ेस वर्शन के बारे में क्वेरी करना

क्लाइंट, उस इंटरफ़ेस के वर्शन और हैश के बारे में क्वेरी कर सकते हैं जिसे रिमोट ऑब्जेक्ट लागू कर रहा है. साथ ही, रिटर्न की गई वैल्यू की तुलना, क्लाइंट के इस्तेमाल किए जा रहे इंटरफ़ेस की वैल्यू से की जा सकती है.

cpp बैकएंड का उदाहरण:

sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();

ndk (और ndk_platform) बैकएंड का उदाहरण:

IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);

java बैकएंड का उदाहरण:

IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
  // the remote side is using an older interface
}

String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();

Java भाषा के लिए, रिमोट साइड को getInterfaceVersion() और getInterfaceHash() को इस तरह लागू करना होगा (IFoo के बजाय super का इस्तेमाल किया जाता है, ताकि कॉपी और चिपकाने से जुड़ी गड़बड़ियां न हों. javac कॉन्फ़िगरेशन के आधार पर, चेतावनियों को बंद करने के लिए, एनोटेशन @SuppressWarnings("static") की ज़रूरत पड़ सकती है):

class MyFoo extends IFoo.Stub {
    @Override
    public final int getInterfaceVersion() { return super.VERSION; }

    @Override
    public final String getInterfaceHash() { return super.HASH; }
}

ऐसा इसलिए होता है, क्योंकि जनरेट की गई क्लास (IFoo, IFoo.Stub वगैरह) को क्लाइंट और सर्वर के बीच शेयर किया जाता है. उदाहरण के लिए, क्लास, बूट क्लासपाथ में हो सकती हैं. क्लास शेयर करने पर, सर्वर को क्लास के नए वर्शन से भी लिंक किया जाता है. भले ही, इसे इंटरफ़ेस के पुराने वर्शन के साथ बनाया गया हो. अगर शेयर किए गए क्लास में यह मेटा इंटरफ़ेस लागू किया जाता है, तो यह हमेशा सबसे नया वर्शन दिखाता है. हालांकि, ऊपर बताए गए तरीके को लागू करने पर, इंटरफ़ेस का वर्शन नंबर सर्वर के कोड में एम्बेड हो जाता है. ऐसा इसलिए होता है, क्योंकि IFoo.VERSION एक ऐसा static final int होता है जिसे रेफ़रंस देने पर इनलाइन किया जाता है. इसलिए, यह तरीका उस सटीक वर्शन को दिखा सकता है जिससे सर्वर बनाया गया था.

पुराने इंटरफ़ेस का इस्तेमाल करना

ऐसा हो सकता है कि किसी क्लाइंट को AIDL इंटरफ़ेस के नए वर्शन पर अपडेट किया गया हो, लेकिन सर्वर पुराने AIDL इंटरफ़ेस का इस्तेमाल कर रहा हो. ऐसे मामलों में, किसी पुराने इंटरफ़ेस पर कोई तरीका कॉल करने पर UNKNOWN_TRANSACTION दिखता है.

स्टेबल AIDL की मदद से, क्लाइंट के पास ज़्यादा कंट्रोल होता है. क्लाइंट साइड में, किसी AIDL इंटरफ़ेस के लिए डिफ़ॉल्ट तौर पर लागू होने की सुविधा सेट की जा सकती है. डिफ़ॉल्ट तौर पर लागू किए गए तरीके को सिर्फ़ तब लागू किया जाता है, जब उस तरीके को रिमोट साइड में लागू न किया गया हो. ऐसा इसलिए होता है, क्योंकि इसे इंटरफ़ेस के पुराने वर्शन के साथ बनाया गया था. डिफ़ॉल्ट वैल्यू, दुनिया भर में सेट की जाती हैं. इसलिए, इनका इस्तेमाल ऐसे कॉन्टेक्स्ट में नहीं किया जाना चाहिए जिन्हें शेयर किया जा सकता है.

Android 13 और उसके बाद के वर्शन में C++ में लिखा गया उदाहरण:

class MyDefault : public IFooDefault {
  Status anAddedMethod(...) {
   // do something default
  }
};

// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());

foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
                         // remote side is not implementing it

Java में उदाहरण:

IFoo.Stub.setDefaultImpl(new IFoo.Default() {
    @Override
    public xxx anAddedMethod(...)  throws RemoteException {
        // do something default
    }
}); // once per an interface in a process

foo.anAddedMethod(...);

आपको किसी AIDL इंटरफ़ेस में, सभी तरीकों को डिफ़ॉल्ट रूप से लागू करने की ज़रूरत नहीं है. जिन तरीकों को रिमोट साइड में लागू करने की गारंटी होती है (क्योंकि आपको पता है कि जब ये तरीके एआईडीएल इंटरफ़ेस के ब्यौरे में थे, तब रिमोट बनाया गया था), उन्हें डिफ़ॉल्ट impl क्लास में बदलने की ज़रूरत नहीं होती.

मौजूदा AIDL को स्ट्रक्चर्ड या स्टेबल AIDL में बदलना

अगर आपके पास कोई मौजूदा AIDL इंटरफ़ेस और उसका इस्तेमाल करने वाला कोड है, तो इंटरफ़ेस को स्टैबल AIDL इंटरफ़ेस में बदलने के लिए, यहां दिए गए निर्देशों का पालन करें.

  1. अपने इंटरफ़ेस की सभी डिपेंडेंसी की पहचान करें. इंटरफ़ेस जिस हर पैकेज पर निर्भर करता है उसके लिए यह तय करें कि पैकेज को स्टैबल एआईडीएल में तय किया गया है या नहीं. अगर इसे तय नहीं किया गया है, तो पैकेज को बदलना होगा.

  2. अपने इंटरफ़ेस में मौजूद सभी पार्सल करने लायक आइटम को, स्थिर पार्सल करने लायक आइटम में बदलें. हालांकि, इंटरफ़ेस की फ़ाइलों में कोई बदलाव नहीं किया जा सकता. इसके लिए, सीधे तौर पर AIDL फ़ाइलों में उनके स्ट्रक्चर को दिखाएं. इन नए टाइप का इस्तेमाल करने के लिए, मैनेजमेंट क्लास को फिर से लिखना होगा. ऐसा, aidl_interface पैकेज (नीचे) बनाने से पहले किया जा सकता है.

  3. ऊपर बताए गए तरीके से aidl_interface पैकेज बनाएं. इसमें आपके मॉड्यूल का नाम, उसकी डिपेंडेंसी, और आपकी ज़रूरत की अन्य जानकारी शामिल होनी चाहिए. डेटा को सिर्फ़ स्ट्रक्चर्ड ही नहीं, बल्कि स्थिर भी बनाया जा सकता है. इसके लिए, डेटा का वर्शन भी बनाना होगा. ज़्यादा जानकारी के लिए, इंटरफ़ेस के वर्शन देखें.