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

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 से पता चलता है कि एनोटेट की गई इकाई की वैल्यू नहीं दी जा सकती.

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

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 हो सकता है.

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

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

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

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

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

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

utf8InCpp

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

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

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

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

VintfStability

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

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

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

@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 टाइप, गैर-एसडीके इंटरफ़ेस का हिस्सा है. यह इंटरफ़ेस, लेगसी ऐप्लिकेशन के लिए उपलब्ध है. छुपे हुए एपीआई के बारे में ज़्यादा जानने के लिए, नॉन-एसडीके इंटरफ़ेस पर पाबंदियां देखें.

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, }

CPP बैकएंड में, यह 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 की मदद से, जांच को तब बदला जा सकता है, जब 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

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

JavaPassthrough

JavaPassthrough की मदद से, जनरेट किए गए Java API को किसी भी 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)

जनरेट किए गए Java कोड में.

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

इस एनोटेशन को किसी भी AIDL इकाई से जोड़ा जा सकता है. यह एनोटेशन, 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, स्ट्रक्चर्ड पार्सलेबल को तय साइज़ के तौर पर मार्क करता है. एक बार मार्क किए जाने के बाद, पार्सल किए जा सकने वाले ऑब्जेक्ट में नए फ़ील्ड नहीं जोड़े जा सकेंगे. पार्सल किए जा सकने वाले ऑब्जेक्ट के सभी फ़ील्ड का साइज़ तय होना चाहिए. इनमें प्रिमिटिव टाइप, एनम, तय साइज़ वाले ऐरे, और FixedSize के तौर पर मार्क किए गए अन्य पार्सल किए जा सकने वाले ऑब्जेक्ट शामिल हैं.

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

ब्यौरा

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

package android.foo;

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

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

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

@hide in comments

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

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

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

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

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