एआईडीएल बैकएंड

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

नीचे दी गई टेबल में, एपीआई प्लैटफ़ॉर्म की स्थिरता का मतलब इस एपीआई सरफ़ेस पर कोड को इस तरह से कंपाइल करने की सुविधा से है कि कोड को system.img libbinder.so बाइनरी से अलग डिलीवर किया जा सके.

एआईडीएल में ये बैकएंड होते हैं:

बैकएंड Language एपीआई प्लैटफ़ॉर्म सिस्टम बनाएं
Java Java SDK/SystemApi (स्टेबल*) सभी
एनडीके C++ libbinder_ndk (स्टेबल*) aidl_इंटरफ़ेस
सीपीपी C++ लिबिंदर (अस्टेबल) सभी
Rust Rust libbinder_rs (स्टेबल*) aidl_इंटरफ़ेस
  • एपीआई के ये प्लैटफ़ॉर्म स्टेबल हैं. हालांकि, सेवा मैनेजमेंट के लिए इस्तेमाल किए जाने वाले कई एपीआई, प्लैटफ़ॉर्म के अंदरूनी इस्तेमाल के लिए रिज़र्व हैं. साथ ही, ये ऐप्लिकेशन के लिए उपलब्ध नहीं हैं. ऐप्लिकेशन में एआईडीएल का इस्तेमाल करने के तरीके के बारे में ज़्यादा जानने के लिए, डेवलपर से जुड़े दस्तावेज़ देखें.
  • Rust बैकएंड को Android 12 में पेश किया गया था. NDK बैकएंड, Android 10 के बाद से उपलब्ध है.
  • रस्ट क्रेट libbinder_ndk के ऊपर बनाया गया है, जिसकी मदद से इसे एक जगह रखा जा सकता है. APEXes उसी तरह बाइंडर क्रेट का इस्तेमाल करते हैं जिस तरह सिस्टम के अन्य लोग करते हैं. Rust के हिस्से को APEX में बंडल करके उसके अंदर भेजा जाता है. यह सिस्टम पार्टीशन पर मौजूद libbinder_ndk.so पर निर्भर करता है.

सिस्टम बनाएं

बैकएंड के आधार पर, स्टब कोड में एआईडीएल को कंपाइल करने के दो तरीके हैं. बिल्ड सिस्टम के बारे में ज़्यादा जानकारी के लिए, Soong Module रेफ़रंस देखें.

कोर बिल्ड सिस्टम

किसी भी cc_ या java_ Android.bp मॉड्यूल में (या उनके Android.mk मिलते-जुलते मॉड्यूल में), .aidl फ़ाइलों को सोर्स फ़ाइलों के तौर पर बताया जा सकता है. इस मामले में, एआईडीएल के Java/सीपीपी बैकएंड का इस्तेमाल किया जाता है, न कि एनडीके बैकएंड का. साथ ही, संबंधित एआईडीएल फ़ाइलों का इस्तेमाल करने वाली क्लास, मॉड्यूल में अपने-आप जुड़ जाती हैं. विकल्प जैसे कि local_include_dirs, जो बिल्ड सिस्टम को मॉड्यूल में मौजूद एआईडीएल फ़ाइलों का रूट पाथ बताता है. इन मॉड्यूल में, aidl: ग्रुप के तहत उन विकल्पों की जानकारी दी जा सकती है. ध्यान दें कि Rust बैकएंड का इस्तेमाल सिर्फ़ Rust के साथ किया जा सकता है. rust_ मॉड्यूल को अलग-अलग तरीके से मैनेज किया जाता है, क्योंकि एआईडीएल फ़ाइलों को सोर्स फ़ाइलों के तौर पर नहीं माना जाता. इसके बजाय, aidl_interface मॉड्यूल <aidl_interface name>-rust नाम का एक rustlib बनाता है, जिसे लिंक किया जा सकता है. ज़्यादा जानकारी के लिए, रस्ट एआईडीएल का उदाहरण देखें.

aidl_इंटरफ़ेस

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

प्रकार

टाइप के लिए, aidl कंपाइलर को रेफ़रंस के तौर पर लागू किया जा सकता है. इंटरफ़ेस बनाते समय, उससे जुड़ी इंटरफ़ेस फ़ाइल देखने के लिए aidl --lang=<backend> ... शुरू करें. aidl_interface मॉड्यूल का इस्तेमाल करने पर, आउटपुट को out/soong/.intermediates/<path to module>/ में देखा जा सकता है.

Java/AIDL टाइप C++ टाइप एनडीके का टाइप रस्ट टाइप
बूलियन बूल बूल बूल
बाइट पूर्णांक पूर्णांक आई8
वर्ण वर्ण16_t वर्ण16_t यू16
आईएनटी पूर्णांक32_t पूर्णांक32_t आई32
लंबा int64_t int64_t आई64
फ़्लोट फ़्लोट फ़्लोट F32
डबल डबल डबल एफ़64
स्ट्रिंग android::स्ट्रिंग16 std::स्ट्रिंग स्ट्रिंग
android.os.पार्स किया जा सकने वाला android::पार्सेबल लागू नहीं लागू नहीं
आईबाइंडर Android::IBinder एनडीके::एसपीएआईबाइंडर बाइंडर::SpIBinder
टी[] std::वेक्टर<T> std::वेक्टर<T> इसमें: &[T]
आउट: Vec<T>
बाइट[] std::वेक्टर<uint8_t> std::vector<int8_t>1 इसमें: &[u8]
आउट: Vec<u8>
सूची<T> std::वेक्टर<T>2 std::वेक्टर<T>3 इसमें: &[T]4
आउट: Vec<T>
फ़ाइल वर्णनकर्ता android::base::unique_fd लागू नहीं बाइंडर::पार्सल::ParcelFileDescriptor
पार्सलफ़ाइलडिस्क्रिप्टर android::os::ParcelFileDescriptor एनडीके::ScopedFileDescriptor बाइंडर::पार्सल::ParcelFileDescriptor
इंटरफ़ेस प्रकार (T) android::sp<T> std::shared_ptr<T>7 बाइंडर::मज़बूत
पार्स किया जा सकने वाला टाइप (T) T T T
यूनियन टाइप (T)5 T T T
टी[N] 6 std::अरे<T, N> std::अरे<T, N> [ट; नहीं]

1. Android 12 या उसके बाद के वर्शन में, बाइट अरै काम करने की वजहों के लिए, int8_t के बजाय uint8_t का इस्तेमाल करते हैं.

2. C++ बैकएंड List<T> के साथ काम करता है, जहां T, String, IBinder, ParcelFileDescriptor या पार्सल करने लायक है. Android 13 या उसके बाद के वर्शन में, T, कलेक्शन को छोड़कर कोई भी नॉन-प्रीमिटिव टाइप (इसमें इंटरफ़ेस टाइप भी शामिल हैं) हो सकता है. एओएसपी का सुझाव है कि आप T[] जैसे कलेक्शन टाइप का इस्तेमाल करें, क्योंकि वे सभी बैकएंड में काम करते हैं.

3. एनडीके बैकएंड, List<T> के साथ काम करता है, जहां T, String, ParcelFileDescriptor या पार्सल में से कोई एक है. Android 13 या उसके बाद के वर्शन में, T, अरे को छोड़कर कोई भी नॉन-प्रीमिटिव टाइप हो सकता है.

4. रस्ट कोड के लिए टाइप अलग-अलग तरीके से पास किए जाते हैं. यह इस बात पर निर्भर करता है कि वे इनपुट (आर्ग्युमेंट) हैं या आउटपुट (लौटी गई वैल्यू).

5. यूनियन के टाइप, Android 12 और उसके बाद के वर्शन में काम करते हैं.

6. Android 13 या उसके बाद वाले वर्शन में, तय साइज़ वाले कलेक्शन काम करते हैं. तय साइज़ वाले कलेक्शन में कई डाइमेंशन हो सकते हैं, जैसे कि int[3][4]. Java बैकएंड में, तय साइज़ वाले अरे को अरे टाइप के तौर पर दिखाया जाता है.

7. बाइंडर SharedRefBase ऑब्जेक्ट को इंस्टैंशिएट करने के लिए, SharedRefBase::make\<My\>(... args ...) का इस्तेमाल करें. यह फ़ंक्शन एक std::shared_ptr\<T\> ऑब्जेक्ट बनाता है, जिसे अंदरूनी तौर पर भी मैनेज किया जाता है. ऐसा तब होता है, जब बाइंडर का मालिकाना हक किसी दूसरी प्रोसेस के पास हो. ऑब्जेक्ट को दूसरे तरीकों से बनाने से, दो बार मालिकाना हक होता है.

दिशा (इन/आउट/इनआउट)

फ़ंक्शन के लिए आर्ग्युमेंट के टाइप बताते समय, उन्हें in, out या inout के तौर पर बताया जा सकता है. इससे यह कंट्रोल किया जाता है कि आईपीसी कॉल के लिए जानकारी किस दिशा में भेजी जाए. in डिफ़ॉल्ट निर्देश है और यह बताता है कि कॉल करने वाले (कॉलर) से कॉली को डेटा भेजा गया है. out का मतलब है कि डेटा को कॉल पाने वाले से कॉलर को भेजा जाता है. inout इन दोनों का कॉम्बिनेशन है. हालांकि, Android टीम का सुझाव है कि आप आर्ग्युमेंट की खास जानकारी देने वाले inout का इस्तेमाल न करें. अगर inout का इस्तेमाल नए वर्शन वाले इंटरफ़ेस और कॉल करने वाले पुराने व्यक्ति के साथ किया जाता है, तो सिर्फ़ कॉलर में मौजूद अतिरिक्त फ़ील्ड, अपनी डिफ़ॉल्ट वैल्यू पर रीसेट हो जाते हैं. Rust के लिए, सामान्य inout टाइप को &mut Vec<T> और inout टाइप को &mut Vec<T> मिलता है.

interface IRepeatExamples {
    MyParcelable RepeatParcelable(MyParcelable token); // implicitly 'in'
    MyParcelable RepeatParcelableWithIn(in MyParcelable token);
    void RepeatParcelableWithInAndOut(in MyParcelable param, out MyParcelable result);
    void RepeatParcelableWithInOut(inout MyParcelable param);
}

UTF8/UTF16

सीपीपी बैकएंड से, यह चुना जा सकता है कि स्ट्रिंग utf-8 है या utf-16. स्ट्रिंग को एआईडीएल में @utf8InCpp String के तौर पर बताएं, ताकि उन्हें utf-8 में अपने-आप बदला जा सके. NDK और Rust बैकएंड हमेशा utf-8 स्ट्रिंग का इस्तेमाल करते हैं. utf8InCpp एनोटेशन के बारे में ज़्यादा जानकारी के लिए, एआईडीएल में एनोटेशन देखें.

शून्य होने की क्षमता

सीपीपी और एनडीके बैकएंड में शून्य वैल्यू दिखाने के लिए, @nullable के साथ उन टाइप की व्याख्या की जा सकती है जो Java बैकएंड में शून्य हो सकती हैं. रस्ट बैकएंड में, इन @nullable टाइप को Option<T> के तौर पर दिखाया जाता है. नेटिव सर्वर डिफ़ॉल्ट रूप से शून्य वैल्यू को अस्वीकार कर देते हैं. इसके लिए, सिर्फ़ interface और IBinder टाइप के अपवाद हैं. एनडीके रीड और सीपीपी/एनडीके राइट के लिए हमेशा शून्य हो सकते हैं. nullable एनोटेशन के बारे में ज़्यादा जानकारी के लिए, एआईडीएल में एनोटेशन देखें.

पसंद के मुताबिक पार्सल किए जा सकने वाले प्रॉडक्ट

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

एआईडीएल को पार्स किए जा सकने वाले कस्टम पैरामीटर के बारे में बताने के लिए, एआईडीएल पार्स किया जा सकने वाला डिक्लेरेशन ऐसा दिखता है:

    package my.pack.age;
    parcelable Foo;

डिफ़ॉल्ट रूप से, यह Java पार्स करने लायक एलान करता है, जहां my.pack.age.Foo Parcelable इंटरफ़ेस को लागू करने वाली Java क्लास है.

एआईडीएल में कस्टम सीपीपी बैकएंड पार्सल करने की जानकारी देने के लिए, cpp_header का इस्तेमाल करें:

    package my.pack.age;
    parcelable Foo cpp_header "my/pack/age/Foo.h";

my/pack/age/Foo.h में C++ लागू करने पर यह दिखाई देगा:

    #include <binder/Parcelable.h>

    class MyCustomParcelable : public android::Parcelable {
    public:
        status_t writeToParcel(Parcel* parcel) const override;
        status_t readFromParcel(const Parcel* parcel) override;

        std::string toString() const;
        friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
        friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
    };

एआईडीएल में पसंद के मुताबिक एनडीके पार्सल करने का एलान करने के लिए, ndk_header का इस्तेमाल करें:

    package my.pack.age;
    parcelable Foo ndk_header "android/pack/age/Foo.h";

android/pack/age/Foo.h में एनडीके लागू करने का तरीका ऐसा दिखता है:

    #include <android/binder_parcel.h>

    class MyCustomParcelable {
    public:

        binder_status_t writeToParcel(AParcel* _Nonnull parcel) const;
        binder_status_t readFromParcel(const AParcel* _Nonnull parcel);

        std::string toString() const;

        friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
        friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
    };

Android 15 (एओएसपी एक्सपेरिमेंट) में, एआईडीएल में पसंद के मुताबिक रुस्ट पार्सल करने की सुविधा का एलान करने के लिए, rust_type का इस्तेमाल करें:

package my.pack.age;
@RustOnlyStableParcelable parcelable Foo rust_type "rust_crate::Foo";

rust_crate/src/lib.rs में Rust लागू करें:

use binder::{
    binder_impl::{BorrowedParcel, UnstructuredParcelable},
    impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
    StatusCode,
};

#[derive(Clone, Debug, Eq, PartialEq)]
struct Foo {
    pub bar: String,
}

impl UnstructuredParcelable for Foo {
    fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
        parcel.write(&self.bar)?;
        Ok(())
    }

    fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
        let bar = parcel.read()?;
        Ok(Self { bar })
    }
}

impl_deserialize_for_unstructured_parcelable!(Foo);
impl_serialize_for_unstructured_parcelable!(Foo);

इसके बाद, पार्स किए जा सकने वाले इस डेटा को एआईडीएल फ़ाइलों में टाइप के तौर पर इस्तेमाल किया जा सकता है. हालांकि, यह एआईडीएल फ़ाइल से जनरेट नहीं होगा. सीपीपी/एनडीके बैकएंड कस्टम पार्सल के लिए, < और == ऑपरेटर उपलब्ध कराएं, ताकि इन्हें union में इस्तेमाल किया जा सके.

डिफ़ॉल्ट वैल्यू

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

    parcelable Foo {
      int numField = 42;
      String stringField = "string value";
      char charValue = 'a';
      ...
    }

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

अन्य बैकएंड में, डिफ़ॉल्ट वैल्यू सेट न होने पर फ़ील्ड को डिफ़ॉल्ट शुरू की गई वैल्यू से शुरू किया जाता है. उदाहरण के लिए, C++ बैकएंड में, String फ़ील्ड को खाली स्ट्रिंग के तौर पर शुरू किया जाता है. साथ ही, List<T> फ़ील्ड को खाली vector<T> के तौर पर शुरू किया जाता है. @nullable फ़ील्ड को शून्य-वैल्यू वाले फ़ील्ड के तौर पर शुरू किया जाता है.

गड़बड़ी ठीक करना

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

गड़बड़ी वाले आउटपुट पैरामीटर

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

गड़बड़ी वाली किन वैल्यू का इस्तेमाल करना है

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

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

Java में, गड़बड़ियां अपवाद जैसे कि android.os.RemoteException से मैप होती हैं. किसी खास सेवा के अपवादों के लिए, Java, उपयोगकर्ता की ओर से तय की गई गड़बड़ी के साथ android.os.ServiceSpecificException का इस्तेमाल करता है.

Android में स्थानीय कोड, अपवादों का इस्तेमाल नहीं करता. सीपीपी बैकएंड, android::binder::Status का इस्तेमाल करता है. NDK बैकएंड, ndk::ScopedAStatus का इस्तेमाल करता है. एआईडीएल से जनरेट होने वाला हर तरीका, इनमें से कोई एक तरीका दिखाता है. इससे, तरीके की स्थिति के बारे में पता चलता है. Rust बैकएंड, एनडीके की तरह ही अपवाद कोड वैल्यू का इस्तेमाल करता है, लेकिन उपयोगकर्ताओं को डिलीवर करने से पहले, यह उन्हें नेटिव Rust गड़बड़ियों (StatusCode, ExceptionCode) में बदल देता है. किसी सेवा से जुड़ी गड़बड़ियों के लिए, Status या ScopedAStatus, उपयोगकर्ता की ओर से तय की गई गड़बड़ी के साथ EX_SERVICE_SPECIFIC का इस्तेमाल करता है.

इन फ़ाइलों में, बिल्ट-इन गड़बड़ियों के टाइप देखे जा सकते हैं:

बैकएंड परिभाषा
Java android/os/Parcel.java
सीपीपी binder/Status.h
एनडीके android/binder_status.h
Rust android/binder_status.h

अलग-अलग बैकएंड का इस्तेमाल करना

ये निर्देश खास तौर पर Android प्लैटफ़ॉर्म कोड के लिए हैं. इन उदाहरणों में, तय किए गए my.package.IFoo टाइप का इस्तेमाल किया गया है. रस्ट बैकएंड का इस्तेमाल करने के तरीके के बारे में जानने के लिए, Android रस्ट पैटर्न वाले पेज पर रस्ट एआईडीएल का उदाहरण देखें.

इंपोर्ट के टाइप

चाहे तय किया गया टाइप कोई इंटरफ़ेस हो, पार्सल किया जा सके या यूनियन हो, तो उसे Java में इंपोर्ट किया जा सकता है:

import my.package.IFoo;

या सीपीपी बैकएंड में:

#include <my/package/IFoo.h>

या एनडीके बैकएंड में (अतिरिक्त aidl नेमस्पेस पर देखें):

#include <aidl/my/package/IFoo.h>

या Rust बैकएंड में:

use my_package::aidl::my::package::IFoo;

हालांकि, Java में नेस्ट किए गए टाइप को इंपोर्ट किया जा सकता है, लेकिन सीपीपी/एनडीके बैकएंड में, आपको इसके रूट टाइप के लिए हेडर शामिल करना होगा. उदाहरण के लिए, my/package/IFoo.aidl में बताए गए Bar नेस्ट किए गए टाइप (IFoo, फ़ाइल का रूट टाइप है) को इंपोर्ट करते समय, आपको सीपीपी बैकएंड के लिए <my/package/IFoo.h> या एनडीके बैकएंड के लिए <aidl/my/package/IFoo.h> को शामिल करना होगा.

सेवाएं लागू करना

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

    package my.package;
    interface IFoo {
        int doFoo();
    }

Java में, आपको इस क्लास से भी विस्तार करना होगा:

    import my.package.IFoo;
    public class MyFoo extends IFoo.Stub {
        @Override
        int doFoo() { ... }
    }

सीपीपी बैकएंड में:

    #include <my/package/BnFoo.h>
    class MyFoo : public my::package::BnFoo {
        android::binder::Status doFoo(int32_t* out) override;
    }

एनडीके बैकएंड में (अतिरिक्त aidl नेमस्पेस पर देखें):

    #include <aidl/my/package/BnFoo.h>
    class MyFoo : public aidl::my::package::BnFoo {
        ndk::ScopedAStatus doFoo(int32_t* out) override;
    }

Rust बैकएंड में:

    use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFoo};
    use binder;

    /// This struct is defined to implement IRemoteService AIDL interface.
    pub struct MyFoo;

    impl Interface for MyFoo {}

    impl IFoo for MyFoo {
        fn doFoo(&self) -> binder::Result<()> {
           ...
           Ok(())
        }
    }

या एक साथ काम नहीं करने वाले रस्ट के साथ:

    use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFooAsyncServer};
    use binder;

    /// This struct is defined to implement IRemoteService AIDL interface.
    pub struct MyFoo;

    impl Interface for MyFoo {}

    #[async_trait]
    impl IFooAsyncServer for MyFoo {
        async fn doFoo(&self) -> binder::Result<()> {
           ...
           Ok(())
        }
    }

रजिस्टर करें और सेवाएं पाएं

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

Java में:

    import android.os.ServiceManager;
    // registering
    ServiceManager.addService("service-name", myService);
    // return if service is started now
    myService = IFoo.Stub.asInterface(ServiceManager.checkService("service-name"));
    // waiting until service comes up (new in Android 11)
    myService = IFoo.Stub.asInterface(ServiceManager.waitForService("service-name"));
    // waiting for declared (VINTF) service to come up (new in Android 11)
    myService = IFoo.Stub.asInterface(ServiceManager.waitForDeclaredService("service-name"));

सीपीपी बैकएंड में:

    #include <binder/IServiceManager.h>
    // registering
    defaultServiceManager()->addService(String16("service-name"), myService);
    // return if service is started now
    status_t err = checkService<IFoo>(String16("service-name"), &myService);
    // waiting until service comes up (new in Android 11)
    myService = waitForService<IFoo>(String16("service-name"));
    // waiting for declared (VINTF) service to come up (new in Android 11)
    myService = waitForDeclaredService<IFoo>(String16("service-name"));

एनडीके बैकएंड में (अतिरिक्त aidl नेमस्पेस पर देखें):

    #include <android/binder_manager.h>
    // registering
    binder_exception_t err = AServiceManager_addService(myService->asBinder().get(), "service-name");
    // return if service is started now
    myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_checkService("service-name")));
    // is a service declared in the VINTF manifest
    // VINTF services have the type in the interface instance name.
    bool isDeclared = AServiceManager_isDeclared("android.hardware.light.ILights/default");
    // wait until a service is available (if isDeclared or you know it's available)
    myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_waitForService("service-name")));

Rust बैकएंड में:

use myfoo::MyFoo;
use binder;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;

fn main() {
    binder::ProcessState::start_thread_pool();
    // [...]
    let my_service = MyFoo;
    let my_service_binder = BnFoo::new_binder(
        my_service,
        BinderFeatures::default(),
    );
    binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
    // Does not return - spawn or perform any work you mean to do before this call.
    binder::ProcessState::join_thread_pool()
}

एक साथ काम नहीं करने वाली रस्ट बैकएंड में, एक थ्रेड वाले रनटाइम के साथ:

use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    binder::ProcessState::start_thread_pool();
    // [...]
    let my_service = MyFoo;
    let my_service_binder = BnFoo::new_async_binder(
        my_service,
        TokioRuntime(Handle::current()),
        BinderFeatures::default(),
    );

    binder::add_service("myservice", my_service_binder).expect("Failed to register service?");

    // Sleeps forever, but does not join the binder threadpool.
    // Spawned tasks will run on this thread.
    std::future::pending().await
}

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

एक से ज़्यादा थ्रेड वाले रनटाइम के साथ, एसिंक्रोनस रस्ट बैकएंड में:

use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;

#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() {
    binder::ProcessState::start_thread_pool();
    // [...]
    let my_service = MyFoo;
    let my_service_binder = BnFoo::new_async_binder(
        my_service,
        TokioRuntime(Handle::current()),
        BinderFeatures::default(),
    );

    binder::add_service("myservice", my_service_binder).expect("Failed to register service?");

    // Sleep forever.
    tokio::task::block_in_place(|| {
        binder::ProcessState::join_thread_pool();
    });
}

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

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

  • Java में, android.os.IBinder::linkToDeath का इस्तेमाल करें.
  • सीपीपी बैकएंड में, android::IBinder::linkToDeath का इस्तेमाल करें.
  • एनडीके बैकएंड में, AIBinder_linkToDeath का इस्तेमाल करें.
  • Rust बैकएंड में, DeathRecipient ऑब्जेक्ट बनाएं. इसके बाद, my_binder.link_to_death(&mut my_death_recipient) को कॉल करें. ध्यान दें कि DeathRecipient के पास कॉलबैक का मालिकाना हक होता है. इसलिए, आपको उस ऑब्जेक्ट को तब तक चालू रखना होगा, जब तक आपको सूचनाएं चाहिए.

कॉल करने वाले (कॉलर) की जानकारी

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

Java बैकएंड में:

    ... = Binder.getCallingPid();
    ... = Binder.getCallingUid();

सीपीपी बैकएंड में:

    ... = IPCThreadState::self()->getCallingPid();
    ... = IPCThreadState::self()->getCallingUid();

एनडीके बैकएंड में:

    ... = AIBinder_getCallingPid();
    ... = AIBinder_getCallingUid();

Rust बैकएंड में, इंटरफ़ेस लागू करते समय, यह जानकारी दें (इसे डिफ़ॉल्ट की अनुमति देने के बजाय):

    ... = ThreadState::get_calling_pid();
    ... = ThreadState::get_calling_uid();

सेवाओं के लिए गड़बड़ी की रिपोर्ट और डीबग करने वाला एपीआई

जब गड़बड़ी की रिपोर्ट चलती हैं (जैसे कि adb bugreport के साथ), तो वे पूरे सिस्टम से जानकारी इकट्ठा करती हैं. इससे अलग-अलग समस्याओं को डीबग करने में मदद मिलती है. एआईडीएल सेवाओं के लिए, गड़बड़ी की रिपोर्ट, सर्विस मैनेजर में रजिस्टर की गई सभी सेवाओं पर बाइनरी dumpsys का इस्तेमाल करती हैं. इससे उन सेवाओं की जानकारी को गड़बड़ी की रिपोर्ट में डाला जाता है. dumpsys SERVICE [ARGS] की सेवा से जानकारी पाने के लिए, कमांडलाइन पर dumpsys का भी इस्तेमाल किया जा सकता है. C++ और Java बैकएंड में, addService में अतिरिक्त आर्ग्युमेंट का इस्तेमाल करके, यह कंट्रोल किया जा सकता है कि सेवाएं किस क्रम में डंप करें. डीबग करते समय किसी सेवा का पीआईडी पाने के लिए भी dumpsys --pid SERVICE का इस्तेमाल किया जा सकता है.

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

Java बैकएंड में:

    @Override
    protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
        @Nullable String[] args) {...}

सीपीपी बैकएंड में:

    status_t dump(int, const android::android::Vector<android::String16>&) override;

एनडीके बैकएंड में:

    binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;

Rust बैकएंड में, इंटरफ़ेस लागू करते समय, यह जानकारी दें (इसे डिफ़ॉल्ट की अनुमति देने के बजाय):

    fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>

डायनैमिक तौर पर इंटरफ़ेस डिस्क्रिप्टर पाएं

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

Java में, आपको कोड के साथ इंटरफ़ेस डिस्क्रिप्टर मिल सकता है, जैसे:

    service = /* get ahold of service object */
    ... = service.asBinder().getInterfaceDescriptor();

सीपीपी बैकएंड में:

    service = /* get ahold of service object */
    ... = IInterface::asBinder(service)->getInterfaceDescriptor();

NDK और Rust बैकएंड में, यह सुविधा काम नहीं करती.

स्टैटिक रूप से इंटरफ़ेस डिस्क्रिप्टर पाएं

कभी-कभी (जैसे, @VintfStability सेवाओं को रजिस्टर करते समय) आपको यह जानने की ज़रूरत होती है कि इंटरफ़ेस डिस्क्रिप्टर क्या है. Java में, ब्यौरे का इस्तेमाल करने के लिए, कोड जोड़ा जा सकता है, जैसे कि:

    import my.package.IFoo;
    ... IFoo.DESCRIPTOR

सीपीपी बैकएंड में:

    #include <my/package/BnFoo.h>
    ... my::package::BnFoo::descriptor

एनडीके बैकएंड में (अतिरिक्त aidl नेमस्पेस पर देखें):

    #include <aidl/my/package/BnFoo.h>
    ... aidl::my::package::BnFoo::descriptor

Rust बैकएंड में:

    aidl::my::package::BnFoo::get_descriptor()

Enum रेंज

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

एआईडीएल में तय की गई ईनम MyEnum के लिए, वैल्यू को इस तरह क्रम में लगाया जाता है.

सीपीपी बैकएंड में:

    ::android::enum_range<MyEnum>()

एनडीके बैकएंड में:

   ::ndk::enum_range<MyEnum>()

Rust बैकएंड में:

    MyEnum::enum_values()

थ्रेड मैनेज करना

प्रोसेस में libbinder के हर इंस्टेंस में एक थ्रेडपूल होता है. इस्तेमाल के ज़्यादातर मामलों में, यह सिर्फ़ एक थ्रेडपूल होना चाहिए, जिसे सभी बैकएंड के साथ शेयर किया जाना चाहिए. सिर्फ़ तब ऐसा हो सकता है, जब वेंडर कोड /dev/vndbinder से बात करने के लिए libbinder की एक और कॉपी लोड करे. यह एक अलग बाइंडर नोड पर है, इसलिए थ्रेडपूल शेयर नहीं किया जाता.

Java बैकएंड के लिए, थ्रेडपूल का सिर्फ़ साइज़ बढ़ाया जा सकता है (क्योंकि यह पहले से ही शुरू हो चुका है):

    BinderInternal.setMaxThreads(<new larger value>);

सीपीपी बैकएंड के लिए, ये काम किए जा सकते हैं:

    // set max threadpool count (default is 15)
    status_t err = ProcessState::self()->setThreadPoolMaxThreadCount(numThreads);
    // create threadpool
    ProcessState::self()->startThreadPool();
    // add current thread to threadpool (adds thread to max thread count)
    IPCThreadState::self()->joinThreadPool();

इसी तरह, एनडीके बैकएंड में:

    bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
    ABinderProcess_startThreadPool();
    ABinderProcess_joinThreadPool();

Rust बैकएंड में:

    binder::ProcessState::start_thread_pool();
    binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
    binder::ProcessState::join_thread_pool();

एक साथ काम नहीं करने वाले Rust बैकएंड के साथ, आपको दो थ्रेडपूल की ज़रूरत होगी: बाइंडर और Tokio. इसका मतलब यह है कि एक साथ काम नहीं करने वाले रस्ट का इस्तेमाल करने वाले ऐप्लिकेशन को खास ध्यान देने की ज़रूरत है, खास तौर पर जब बात join_thread_pool का इस्तेमाल करने की हो. इस बारे में ज़्यादा जानकारी के लिए, सेवाओं के रजिस्ट्रेशन से जुड़ा सेक्शन देखें.

रिज़र्व किए गए नाम

C++, Java और Rust, कुछ नामों को कीवर्ड के रूप में या भाषा के खास इस्तेमाल के लिए रिज़र्व रखते हैं. हालांकि, एआईडीएल भाषा के नियमों के हिसाब से पाबंदियां लागू नहीं करता है, लेकिन रिज़र्व किए गए नाम से मेल खाने वाले फ़ील्ड या टाइप नेम का इस्तेमाल करने पर, C++ या Java के लिए कंपाइलेशन काम नहीं किया जा सकता है. Rust के लिए, "रॉ आइडेंटिफ़ायर" सिंटैक्स का इस्तेमाल करके फ़ील्ड या टाइप का नाम बदला जाता है. इसे r# प्रीफ़िक्स का इस्तेमाल करके ऐक्सेस किया जा सकता है.

हमारा सुझाव है कि जहां भी हो सके, अपनी एआईडीएल परिभाषाओं में रिज़र्व किए गए नामों का इस्तेमाल न करें. ऐसा करके, बिना किसी क्रम के बाइंडिंग या एक साथ कंपाइलेशन के काम न करने से बचा जा सकता है.

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

इन नामों से बचें: