एआईडीएल में एनोटेशन

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

इसका सिंटैक्स, Java के सिंटैक्स से मिलता-जुलता है:

@AnnotationName(argument1=value, argument2=value) AidlEntity

यहां AnnotationName एनोटेशन का नाम है और AidlEntity interface Foo, void method() या int arg जैसी एआईडीएल इकाई है. एनोटेशन, उसके बाद आने वाली इकाई से जुड़ा होता है.

कुछ एनोटेशन में, ब्रैकेट के अंदर आर्ग्युमेंट सेट किए जा सकते हैं, जैसा कि ऊपर दिखाया गया है. जिन एनोटेशन में कोई आर्ग्युमेंट नहीं होता उनके लिए ब्रैकेट की ज़रूरत नहीं होती. उदाहरण के लिए:

@AnnotationName AidlEntity

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

पहले से तय किए गए AIDL एनोटेशन की सूची यहां दी गई है:

एनोटेशन Android वर्शन में जोड़ा गया
nullable 7
utf8InCpp 7
VintfStability 11
UnsupportedAppUsage 10
Hide 11
Backing 11
NdkOnlyStableParcelable 14
JavaOnlyStableParcelable 11
JavaDerive 12
JavaPassthrough 12
FixedSize 12
Descriptor 12

nullable

nullable से पता चलता है कि एनोटेट की गई इकाई की वैल्यू नहीं दी जा सकती.

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

interface IFoo {
    // method return types
    @nullable Data method();

    // method parameters
    void method2(in @nullable Data d);
}

parcelable Data {
    // parcelable fields
    @nullable Data d;
}

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

void method(in @nullable int a); // int is a primitive type

यह एनोटेशन, Java बैकएंड के लिए काम नहीं करता. इसकी वजह यह है कि Java में, सभी नॉन-प्राइमटिव टाइप को रेफ़रंस के ज़रिए पास किया जाता है, जो null हो सकता है.

सीपीपी बैकएंड में, @nullable T, Android 11 या उससे पहले के वर्शन में std::unique_ptr<T> पर मैप होता है. साथ ही, Android 12 या उसके बाद के वर्शन में std::optional<T> पर मैप होता है.

NDK बैकएंड में, @nullable T हमेशा std::optional<T> पर मैप होता है.

Rust बैकएंड में, @nullable T हमेशा Option<T> पर मैप होता है.

T[] या List<T> जैसी सूची वाले टाइप L के लिए, @nullable L std::optional<std::vector<std::optional<T>>> पर मैप होता है. इसके अलावा, Android 11 या उससे पहले के वर्शन के लिए सीपीपी बैकएंड के मामले में, @nullable L std::unique_ptr<std::vector<std::unique_ptr<T>>> पर मैप होता है.

हालांकि, इस मैपिंग में एक अपवाद है. जब T IBinder या AIDL इंटरफ़ेस है, तो Rust के अलावा सभी बैकएंड के लिए @nullable काम नहीं करता. दूसरे शब्दों में, @nullable IBinder और IBinder, दोनों android::sp<IBinder> पर बराबर मैप होते हैं. यह वैल्यू पहले से ही शून्य हो सकती है, क्योंकि यह एक स्ट्रॉन्ग पॉइंटर है. सीपीपी में, वैल्यू शून्य हो सकती है, लेकिन टाइप अब भी android::sp<IBinder> है. Rust में, ये टाइप सिर्फ़ तब nullable होते हैं, जब उन्हें @nullable के साथ एनोटेट किया गया हो. एनोटेट किए जाने पर, ये Option<T> पर मैप होते हैं.

Android 13 से, @nullable(heap=true) का इस्तेमाल, रीकर्सिव टाइप को मॉडल करने के लिए, पैकेज किए जा सकने वाले फ़ील्ड के लिए किया जा सकता है. @nullable(heap=true) का इस्तेमाल, तरीकों के पैरामीटर या रिटर्न टाइप के साथ नहीं किया जा सकता. इस एनोटेशन का इस्तेमाल करने पर, फ़ील्ड को सीपीपी/एनडीके बैकएंड में, ढेर में असाइन किए गए रेफ़रंस std::unique_ptr<T> से मैप किया जाता है. @nullable(heap=true), Java बैकएंड में काम नहीं करता.

utf8InCpp

utf8InCpp से पता चलता है कि String को सीपीपी के बैकएंड के लिए, UTF8 फ़ॉर्मैट में दिखाया गया है. जैसा कि नाम से पता चलता है, एनोटेशन अन्य बैकएंड के लिए काम नहीं करता. खास तौर पर, String हमेशा Java बैकएंड में UTF16 और NDK बैकएंड में UTF8 होता है.

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

सीपीपी बैकएंड के लिए, एआईडीएल में @utf8InCpp String, std::string पर मैप होता है. वहीं, एनोटेशन के बिना String, android::String16 पर मैप होता है, जहां UTF16 का इस्तेमाल किया जाता है.

ध्यान दें कि utf8InCpp एनोटेशन मौजूद होने से, तार पर स्ट्रिंग भेजने के तरीके में कोई बदलाव नहीं होता. स्ट्रिंग को हमेशा UTF16 के तौर पर ट्रांसफ़र किया जाता है. एनोटेट की गई utf8InCpp स्ट्रिंग को ट्रांसमिट करने से पहले, UTF16 में बदल दिया जाता है. जब कोई स्ट्रिंग मिलती है, तो अगर उसे utf8InCpp के तौर पर एनोटेट किया गया है, तो उसे UTF16 से UTF8 में बदल दिया जाता है.

VintfStability

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

एनोटेशन से टाइप के हस्ताक्षर में बदलाव नहीं होता. हालांकि, इसे सेट करने पर, टाइप के इंस्टेंस को स्थिर के तौर पर मार्क किया जाता है, ताकि वह वेंडर और सिस्टम की प्रोसेस में शामिल हो सके.

एनोटेशन को सिर्फ़ उपयोगकर्ता के तय किए गए टाइप के एलान से अटैच किया जा सकता है, जैसा कि यहां दिखाया गया है:

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

जब किसी टाइप को VintfStability के साथ एनोटेट किया जाता है, तो टाइप में रेफ़र किए गए किसी भी दूसरे टाइप को भी उसी तरह एनोटेट किया जाना चाहिए. नीचे दिए गए उदाहरण में, Data और IBar, दोनों को VintfStability के साथ एनोटेट किया जाना चाहिए.

@VintfStability
interface IFoo {
    void doSomething(in IBar b); // references IBar
    void doAnother(in Data d); // references Data
}

@VintfStability // required
interface IBar {...}

@VintfStability // required
parcelable Data {...}

इसके अलावा, VintfStability के साथ एनोटेट की गई टाइप की जानकारी देने वाली AIDL फ़ाइलों को सिर्फ़ aidl_interface Soong मॉड्यूल टाइप का इस्तेमाल करके बनाया जा सकता है. साथ ही, stability प्रॉपर्टी को "vintf" पर सेट करना ज़रूरी है.

aidl_interface {
    name: "my_interface",
    srcs: [...],
    stability: "vintf",
}

UnsupportedAppUsage

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

UnsupportedAppUsage एनोटेशन से, जनरेट किए गए कोड के व्यवहार पर कोई असर नहीं पड़ता. एनोटेशन, जनरेट की गई Java क्लास पर सिर्फ़ उसी नाम के Java एनोटेशन के साथ एनोटेट करता है.

// in AIDL
@UnsupportedAppUsage
interface IFoo {...}

// in Java
@android.compat.annotation.UnsupportedAppUsage
public interface IFoo {...}

यह सुविधा, Java के अलावा किसी दूसरे बैकएंड के लिए काम नहीं करती.

बैकिंग

Backing एनोटेशन, AIDL enum टाइप के स्टोरेज टाइप की जानकारी देता है.

@Backing(type="int")
enum Color { RED, BLUE, }

सीपीपी बैकएंड में, यह int32_t टाइप की C++ enum क्लास को उत्सर्जित करता है.

enum class Color : int32_t {
    RED = 0,
    BLUE = 1,
}

अगर एनोटेशन नहीं दिया गया है, तो type को byte माना जाता है. यह सीपीपी बैकएंड के लिए int8_t पर मैप होता है.

type आर्ग्युमेंट को सिर्फ़ इन इंटिग्रल टाइप पर सेट किया जा सकता है:

  • byte (8-बिट चौड़ा)
  • int (32-बिट वाइड)
  • long (64-बिट वाइड)

NdkOnlyStableParcelable

NdkOnlyStableParcelable, पार्सल किए जा सकने वाले एलान (परिभाषा नहीं) को स्थिर के तौर पर मार्क करता है, ताकि इसका रेफ़रंस, स्थिर अन्य AIDL टाइप से लिया जा सके. यह JavaOnlyStableParcelable जैसा ही है, लेकिन NdkOnlyStableParcelable, पार्सल किए जा सकने वाले एलान को Java के बजाय NDK बैकएंड के लिए, स्थिर के तौर पर मार्क करता है.

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

  • आपको ndk_header की वैल्यू देनी होगी.
  • आपके पास ऐसी NDK लाइब्रेरी होनी चाहिए जिसमें पार्सल करने की सुविधा के बारे में बताया गया हो. साथ ही, लाइब्रेरी को लाइब्रेरी में संकलित किया जाना चाहिए. उदाहरण के लिए, cc_* मॉड्यूल के कोर बिल्ड सिस्टम में, static_libs या shared_libs का इस्तेमाल करें. aidl_interface के लिए, Android.bp में additional_shared_libraries में लाइब्रेरी जोड़ें.

JavaOnlyStableParcelable

JavaOnlyStableParcelable, पार्सल किए जा सकने वाले एलान (परिभाषा नहीं) को स्थिर के तौर पर मार्क करता है, ताकि इसका रेफ़रंस, स्थिर अन्य AIDL टाइप से लिया जा सके.

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

parcelable Data { // Data is a structured parcelable.
    int x;
    int y;
}

parcelable AnotherData { // AnotherData is also a structured parcelable
    Data d; // OK, because Data is a structured parcelable
}

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

parcelable Data; // Data is NOT a structured parcelable

parcelable AnotherData {
    Data d; // Error
}

JavaOnlyStableParcelable की मदद से, उस Parcelable के लिए जांच को बदला जा सकता है जिसका रेफ़रंस दिया जा रहा है. ऐसा तब किया जा सकता है, जब वह Android SDK टूल के हिस्से के तौर पर पहले से ही सुरक्षित रूप से उपलब्ध हो.

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

JavaDerive, Java बैकएंड में पार्सल किए जा सकने वाले टाइप के लिए, अपने-आप तरीके जनरेट करता है.

@JavaDerive(equals = true, toString = true)
parcelable Data {
  int number;
  String str;
}

एनोटेशन में क्या जनरेट करना है, यह कंट्रोल करने के लिए ज़्यादा पैरामीटर की ज़रूरत होती है. ये पैरामीटर काम करते हैं:

  • equals=true, equals और hashCode तरीके जनरेट करता है.
  • toString=true, toString का तरीका जनरेट करता है, जो टाइप और फ़ील्ड का नाम प्रिंट करता है. उदाहरण के लिए: Data{number: 42, str: foo}

JavaDefault

Android 13 में जोड़ा गया JavaDefault, यह कंट्रोल करता है कि setDefaultImpl के लिए, डिफ़ॉल्ट तौर पर वर्शनिंग लागू करने की सुविधा जनरेट की जाए या नहीं. जगह बचाने के लिए, यह सुविधा अब डिफ़ॉल्ट रूप से जनरेट नहीं होती.

JavaPassthrough

JavaPassthrough की मदद से, जनरेट किए गए Java एपीआई को किसी भी तरह के Java एनोटेशन के साथ एनोटेट किया जा सकता है.

AIDL में ये एनोटेशन

@JavaPassthrough(annotation="@android.annotation.Alice")
@JavaPassthrough(annotation="@com.android.Alice(arg=com.android.Alice.Value.A)")

बनना

@android.annotation.Alice
@com.android.Alice(arg=com.android.Alice.Value.A)

कोड में दिखेगा.

annotation पैरामीटर की वैल्यू को सीधे तौर पर उत्सर्जित किया जाता है. AIDL कंपाइलर, पैरामीटर की वैल्यू की जांच नहीं करता. अगर Java-लेवल पर सिंटैक्स से जुड़ी कोई गड़बड़ी है, तो उसे AIDL कंपाइलर नहीं, बल्कि Java कंपाइलर पकड़ेगा.

इस एनोटेशन को किसी भी एआईडीएल इकाई से जोड़ा जा सकता है. यह एनोटेशन, Java बैकएंड के लिए काम नहीं करता.

RustDerive

RustDerive, जनरेट किए गए Rust टाइप के लिए, ट्रेट अपने-आप लागू करता है.

एनोटेशन में क्या जनरेट करना है, यह कंट्रोल करने के लिए ज़्यादा पैरामीटर की ज़रूरत होती है. ये पैरामीटर काम करते हैं:

  • Copy=true
  • Clone=true
  • Ord=true
  • PartialOrd=true
  • Eq=true
  • PartialEq=true
  • Hash=true

इन विशेषताओं के बारे में जानने के लिए, https://doc.rust-lang.org पर जाएं.

FixedSize

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

इससे अलग-अलग बिटनेस के लिए कोई गारंटी नहीं मिलती. साथ ही, अलग-अलग बिटनेस वाले कम्यूनिकेशन के लिए, इस पर भरोसा नहीं किया जाना चाहिए.

डिस्क्रिप्टर

Descriptor, किसी इंटरफ़ेस के इंटरफ़ेस डिस्क्रिप्टर को ज़बरदस्ती तय करता है.

package android.foo;

@Descriptor(value="android.bar.IWorld")
interface IHello {...}

इस इंटरफ़ेस का डिस्क्रिप्टर android.bar.IWorld है. अगर Descriptor एनोटेशन मौजूद नहीं है, तो डिस्क्रिप्टर के तौर पर android.foo.IHello का इस्तेमाल किया जाएगा.

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

@hide in comments

AIDL कंपाइलर, टिप्पणियों में @hide की पहचान करता है और इसे मेटलवा को पिकअप करने के लिए, Java आउटपुट के ज़रिए पास करता है. इस टिप्पणी से यह पक्का होता है कि Android के बिल्ड सिस्टम को पता है कि AIDL एपीआई, SDK टूल के एपीआई नहीं हैं.

टिप्पणियों में @deprecated

AIDL कंपाइलर, टिप्पणियों में @deprecated को टैग के तौर पर पहचानता है. इससे, उस एआईडीएल इकाई की पहचान की जाती है जिसका अब इस्तेमाल नहीं किया जाना चाहिए.

interface IFoo {
  /** @deprecated use bar() instead */
  void foo();
  void bar();
}

हर बैकएंड, बंद की गई इकाइयों को बैकएंड के हिसाब से एनोटेशन या एट्रिब्यूट से मार्क करता है, ताकि क्लाइंट को बंद की गई इकाइयों का रेफ़रंस देने पर चेतावनी दी जा सके. उदाहरण के लिए, @Deprecated एनोटेशन और @deprecated टैग, Java से जनरेट किए गए कोड से जुड़े होते हैं.