स्टेबल एआईडीएल

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

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

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

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

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

एआईडीएल इंटरफ़ेस तय करना

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: यह एआईडीएल इंटरफ़ेस मॉड्यूल का नाम है. इससे किसी एआईडीएल इंटरफ़ेस की खास पहचान होती है.
  • srcs: इंटरफ़ेस बनाने वाली एआईडीएल सोर्स फ़ाइलों की सूची. पैकेज com.acme में तय किए गए एआईडीएल टाइप Foo का पाथ <base_path>/com/acme/Foo.aidl पर होना चाहिए. यहां <base_path>, उस डायरेक्ट्री से जुड़ी कोई भी डायरेक्ट्री हो सकती है जहां Android.bp मौजूद है. ऊपर दिए गए उदाहरण में, <base_path> srcs/aidl है.
  • local_include_dir: वह पाथ जहां से पैकेज का नाम शुरू होता है. यह ऊपर बताए गए <base_path> से मेल खाता है.
  • imports: aidl_interface मॉड्यूल की सूची, जिनका इस्तेमाल यह करता है. अगर आपके किसी AIDL इंटरफ़ेस में, किसी दूसरे 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 के इस वर्शन ने इंपोर्ट किया है. एआईडीएल इंटरफ़ेस 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: यह एक वैकल्पिक फ़्लैग है. इसका इस्तेमाल, इस इंटरफ़ेस को वीएनडीके का हिस्सा बनाने के लिए किया जाता है. डिफ़ॉल्ट वैल्यू 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: यह एक वैकल्पिक फ़्लैग है. जनरेट की गई लाइब्रेरी को एसडीके के बजाय प्लैटफ़ॉर्म एपीआई के हिसाब से बनाना हो, तो इसे true पर सेट किया जाना चाहिए.

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

AIDL फ़ाइलें लिखना

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

// 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, versions_with_info प्रॉपर्टी को भी अपडेट करता है, ताकि अतिरिक्त वर्शन और वर्शन के लिए imports दिख सके. असल में, versions_with_info में मौजूद imports को imports फ़ील्ड से कॉपी किया जाता है. हालांकि, इंपोर्ट करने के लिए versions_with_info में versions_with_info में मौजूद सबसे नए स्टेबल वर्शन की जानकारी दी गई है. इसमें कोई वर्शन नहीं है.imports versions_with_info प्रॉपर्टी तय करने के बाद, बिल्ड सिस्टम फ़्रोज़न वर्शन और टॉप ऑफ़ ट्री (टीओटी) के बीच कंपैटिबिलिटी की जांच करता है. साथ ही, यह टीओटी और फ़्रोज़न वर्शन के बीच भी कंपैटिबिलिटी की जांच करता है.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

पार्सल किए जा सकने वाले ऑब्जेक्ट

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

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

एनम और कॉन्स्टेंट

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

यूनियन

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

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

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

जब कोई मॉड्यूल, एक ही 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_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 बिल्ड फ़्लैग

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

कंपैटिबिलिटी मैट्रिक्स और मेनिफ़ेस्ट

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

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

मैट्रिक्स

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

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

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

मेनिफ़ेस्ट

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

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

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

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 के नए स्टेबल इंटरफ़ेस

जब कोई नया एआईडीएल इंटरफ़ेस पैकेज जोड़ा जाता है, तो कोई पिछला फ़्रीज़ किया गया वर्शन नहीं होता. इसलिए, जब 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() के साथ तो नहीं जोड़ा गया है. अगर यह जानकारी दी गई है और रजिस्टर नहीं हो सका, तो प्रोसेस बंद कर दें. अगर इसे ज़ाहिर नहीं किया जाता है, तो रजिस्टर करने में समस्या आ सकती है.

VINTF के स्टेबल एक्सटेंशन इंटरफ़ेस

नए एक्सटेंशन इंटरफ़ेस का कोई पिछला वर्शन नहीं है. साथ ही, ServiceManager के साथ रजिस्टर न होने या VINTF मेनिफ़ेस्ट में एलान न किए जाने की वजह से, IServiceManager::isDeclared() का इस्तेमाल यह तय करने के लिए नहीं किया जा सकता कि एक्सटेंशन इंटरफ़ेस को किसी दूसरे इंटरफ़ेस से कब अटैच करना है.

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

vts_treble_vintf_vendor_test और vts_treble_vintf_framework_test वीटीएस टेस्ट से यह पता चलता है कि रिलीज़ किए गए डिवाइस में, अनफ़्रीज़ किए गए एक्सटेंशन इंटरफ़ेस का इस्तेमाल कब किया गया है. साथ ही, इससे गड़बड़ी का पता चलता है.

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

कटलफ़िश को डेवलपमेंट टूल के तौर पर इस्तेमाल करना

VINTF फ़्रीज़ होने के बाद, हम हर साल फ़्रेमवर्क कंपैटिबिलिटी मैट्रिक्स (एफ़सीएम) 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_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 बन जाता है

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

  • वर्शन 1 के आधार पर: foo-V1-(cpp|ndk|ndk_platform|rust).so
  • वर्शन 2 के आधार पर: 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 में, स्टेबल एआईडीएल के लिए कई मेटा इंटरफ़ेस तरीके जोड़े गए हैं.

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

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

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

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

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

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

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

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