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

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

एआईडीएल के निम्नलिखित बैकएंड हैं:

बैकएंड भाषा एपीआई सतह सिस्टम बनाएं
जावा जावा एसडीके/सिस्टमएपीआई (स्थिर*) सभी
एनडीके सी++ libbinder_ndk (स्थिर*) सहायता_इंटरफ़ेस
सीपीपी सी++ लिबबिंडर (अस्थिर) सभी
जंग जंग libbinder_rs (अस्थिर) सहायता_इंटरफ़ेस
  • ये एपीआई सतहें स्थिर हैं, लेकिन कई एपीआई, जैसे कि सेवा प्रबंधन के लिए, आंतरिक प्लेटफ़ॉर्म उपयोग के लिए आरक्षित हैं और ऐप्स के लिए उपलब्ध नहीं हैं। ऐप्स में एआईडीएल का उपयोग कैसे करें, इस बारे में अधिक जानकारी के लिए डेवलपर दस्तावेज़ देखें।
  • रस्ट बैकएंड को Android 12 में पेश किया गया था; एनडीके बैकएंड एंड्रॉइड 10 के रूप में उपलब्ध है।
  • रस्ट क्रेट libbinder_ndk के शीर्ष पर बनाया गया है। एपेक्स बाइंडर क्रेट का उसी तरह उपयोग करते हैं जैसे सिस्टम साइड पर कोई अन्य करता है। जंग वाले हिस्से को एपेक्स में बंडल किया जाता है और उसके अंदर भेज दिया जाता है। यह सिस्टम विभाजन पर libbinder_ndk.so पर निर्भर करता है।

सिस्टम बनाएं

बैकएंड के आधार पर, एआईडीएल को स्टब कोड में संकलित करने के दो तरीके हैं। बिल्ड सिस्टम पर अधिक जानकारी के लिए, सूंग मॉड्यूल संदर्भ देखें।

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

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

सहायता_इंटरफ़ेस

इस बिल्ड सिस्टम के साथ उपयोग किए जाने वाले प्रकारों को संरचित किया जाना चाहिए। संरचित होने के लिए, पार्सलेबल्स में सीधे फ़ील्ड शामिल होने चाहिए और लक्ष्य भाषाओं में सीधे परिभाषित प्रकारों की घोषणा नहीं होनी चाहिए। स्थिर एआईडीएल के साथ संरचित एआईडीएल कैसे फिट बैठता है, इसके लिए संरचित बनाम स्थिर एआईडीएल देखें।

प्रकार

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

जावा/एआईडीएल प्रकार सी++ प्रकार एनडीके प्रकार जंग का प्रकार
बूलियन बूल बूल बूल
बाइट int8_t int8_t मैं8
चार char16_t char16_t यू 16
int यहाँ int32_t int32_t i32
लंबा int64_t int64_t i64
तैरना तैरना तैरना f32
दोहरा दोहरा दोहरा f64
डोरी एंड्रॉइड::स्ट्रिंग16 एसटीडी::स्ट्रिंग डोरी
android.os.पार्सल करने योग्य एंड्रॉइड::पार्सल करने योग्य एन/ए एन/ए
आईबाइंडर एंड्रॉइड::आईबाइंडर ndk::SpAIBinder बाइंडर::स्पाइबाइंडर
टी[] std::वेक्टर<T> std::वेक्टर<T> में: &[टी]
बाहर: Vec<T>
बाइट[] std::वेक्टर<uint8_t> std::vector<int8_t> 1 में: &[u8]
बाहर: Vec<u8>
सूची<टी> std::vector<T> 2 std::vector<T> 3 इन: &[टी] 4
बाहर: Vec<T>
फाइल डिस्क्रिप्टर android::base::unique_fd एन/ए बाइंडर::पार्सल::पार्सलफाइलडिस्क्रिप्टर
पार्सलफ़ाइल डिस्क्रिप्टर android::os::ParcelFileDescriptor ndk::ScopedFileDescriptor बाइंडर::पार्सल::पार्सलफाइलडिस्क्रिप्टर
इंटरफ़ेस प्रकार (टी) एंड्रॉइड::एसपी<टी> std::shared_ptr<T> बाइंडर::मजबूत
पार्सल योग्य प्रकार (टी) टी टी टी
संघ प्रकार (टी) 5 टी टी टी
टी[एन] 6 std::array<T, N> std::array<T, N> [टी; एन]

1. एंड्रॉइड 12 या उच्चतर में, बाइट सरणियाँ संगतता कारणों से int8_t के बजाय uint8_t का उपयोग करती हैं।

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

3. एनडीके बैकएंड List<T> सपोर्ट करता है जहां T String , ParcelFileDescriptor या पार्सल करने योग्य में से एक है। Android 13 या उच्चतर में, T सरणियों को छोड़कर कोई भी गैर-आदिम प्रकार हो सकता है।

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

5. यूनियन प्रकार एंड्रॉइड 12 और उच्चतर में समर्थित हैं।

6. एंड्रॉइड 13 या उच्चतर में, निश्चित आकार के ऐरे समर्थित हैं। निश्चित आकार के सरणियों के कई आयाम हो सकते हैं (उदाहरण के लिए int[3][4] )। जावा बैकएंड में, निश्चित आकार के सरणियों को सरणी प्रकारों के रूप में दर्शाया जाता है।

दिशात्मकता (अंदर/बाहर/अंदर)

फ़ंक्शंस के तर्कों के प्रकार निर्दिष्ट करते समय, आप उन्हें in , out , या inout के रूप में निर्दिष्ट कर सकते हैं। यह नियंत्रित करता है कि आईपीसी कॉल के लिए जानकारी किस दिशा में भेजी जाती है। in डिफ़ॉल्ट दिशा है, और यह इंगित करता है कि डेटा कॉलर से कैली तक भेजा गया है। out का मतलब है कि डेटा कॉल प्राप्तकर्ता से कॉल करने वाले तक भेजा जाता है। inout इन दोनों का संयोजन है। हालाँकि, एंड्रॉइड टीम अनुशंसा करती है कि आप तर्क विनिर्देशक inout उपयोग करने से बचें। यदि आप एक संस्करणित इंटरफ़ेस और एक पुराने कैली के साथ inout उपयोग करते हैं, तो अतिरिक्त फ़ील्ड जो केवल कॉलर में मौजूद हैं, उनके डिफ़ॉल्ट मानों पर रीसेट हो जाते हैं। रस्ट के संबंध में, एक सामान्य inout प्रकार &mut Vec<T> प्राप्त करता है, और एक सूची inout प्रकार &mut Vec<T> प्राप्त करता है।

यूटीएफ8/यूटीएफ16

CPP बैकएंड से आप चुन सकते हैं कि स्ट्रिंग्स utf-8 हैं या utf-16। स्वचालित रूप से उन्हें utf-8 में परिवर्तित करने के लिए AIDL में स्ट्रिंग्स को @utf8InCpp String के रूप में घोषित करें। एनडीके और रस्ट बैकएंड हमेशा यूटीएफ-8 स्ट्रिंग्स का उपयोग करते हैं। utf8InCpp एनोटेशन के बारे में अधिक जानकारी के लिए, AIDL में एनोटेशन देखें।

अशक्तता

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

कस्टम पार्सलेबल्स

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

एक कस्टम पार्सलेबल घोषित करने के लिए ताकि एआईडीएल को इसके बारे में पता चले, एआईडीएल पार्सलेबल घोषणा इस तरह दिखती है:

    package my.pack.age;
    parcelable Foo;

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

AIDL में पार्सल योग्य कस्टम CPP बैकएंड की घोषणा के लिए, 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 में NDK कार्यान्वयन इस तरह दिखता है:

    #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);
    };

एंड्रॉइड 15 (एओएसपी प्रयोगात्मक) में, एआईडीएल में पार्सल करने योग्य कस्टम रस्ट की घोषणा के लिए, rust_type उपयोग करें:

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

rust_crate/src/lib.rs में रस्ट कार्यान्वयन इस तरह दिखता है:

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';
      ...
    }

जावा बैकएंड में जब डिफ़ॉल्ट मान गायब होते हैं, तो फ़ील्ड को आदिम प्रकारों के लिए शून्य मान और गैर-आदिम प्रकारों के लिए null के रूप में प्रारंभ किया जाता है।

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

त्रुटि प्रबंधन

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

त्रुटियों के साथ आउटपुट पैरामीटर

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

किस त्रुटि मान का उपयोग करना है

कई अंतर्निहित त्रुटि मानों का उपयोग किसी भी एआईडीएल इंटरफेस में किया जा सकता है, लेकिन कुछ का इलाज एक विशेष तरीके से किया जाता है। उदाहरण के लिए, EX_UNSUPPORTED_OPERATION और EX_ILLEGAL_ARGUMENT का उपयोग करना ठीक है जब वे त्रुटि स्थिति का वर्णन करते हैं, लेकिन EX_TRANSACTION_FAILED उपयोग नहीं किया जाना चाहिए क्योंकि इसे अंतर्निहित बुनियादी ढांचे द्वारा विशेष माना जाता है। इन अंतर्निहित मूल्यों पर अधिक जानकारी के लिए बैकएंड विशिष्ट परिभाषाओं की जाँच करें।

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

जावा में, त्रुटियाँ android.os.RemoteException जैसे अपवादों पर मैप होती हैं। सेवा-विशिष्ट अपवादों के लिए, जावा उपयोगकर्ता-परिभाषित त्रुटि के साथ android.os.ServiceSpecificException का उपयोग करता है।

एंड्रॉइड में नेटिव कोड अपवादों का उपयोग नहीं करता है। सीपीपी बैकएंड android::binder::Status उपयोग करता है। एनडीके बैकएंड ndk::ScopedAStatus उपयोग करता है। एआईडीएल द्वारा उत्पन्न प्रत्येक विधि इनमें से एक लौटाती है, जो विधि की स्थिति का प्रतिनिधित्व करती है। रस्ट बैकएंड एनडीके के समान अपवाद कोड मानों का उपयोग करता है, लेकिन उपयोगकर्ता को वितरित करने से पहले उन्हें मूल रस्ट त्रुटियों ( StatusCode , ExceptionCode ) में परिवर्तित करता है। सेवा-विशिष्ट त्रुटियों के लिए, लौटाई गई Status या ScopedAStatus उपयोगकर्ता-परिभाषित त्रुटि के साथ EX_SERVICE_SPECIFIC का उपयोग करता है।

अंतर्निहित त्रुटि प्रकार निम्नलिखित फ़ाइलों में पाए जा सकते हैं:

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

विभिन्न बैकएंड का उपयोग करना

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

आयात के प्रकार

चाहे परिभाषित प्रकार इंटरफ़ेस हो, पार्सल करने योग्य हो, या यूनियन हो, आप इसे जावा में आयात कर सकते हैं:

import my.package.IFoo;

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

#include <my/package/IFoo.h>

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

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

या रस्ट बैकएंड में:

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

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

सेवाएँ कार्यान्वित करना

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

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

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

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

रस्ट बैकएंड में:

    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(())
        }
    }

या async जंग के साथ:

    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(())
        }
    }

पंजीकरण करना और सेवाएँ प्राप्त करना

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

जावा में:

    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")));

रस्ट बैकएंड में:

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::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();
    });
}

मल्टी-थ्रेडेड टोकियो रनटाइम के साथ, उत्पन्न कार्य मुख्य थ्रेड पर निष्पादित नहीं होते हैं। इसलिए, मुख्य थ्रेड पर join_thread_pool कॉल करना अधिक समझ में आता है ताकि मुख्य थ्रेड निष्क्रिय न रहे। एसिंक संदर्भ को छोड़ने के लिए आपको कॉल को block_in_place में लपेटना होगा।

जब बाइंडर होस्ट करने वाली सेवा समाप्त हो जाती है तो आप एक अधिसूचना प्राप्त करने का अनुरोध कर सकते हैं। यह कॉलबैक प्रॉक्सी को लीक होने से बचाने या त्रुटि पुनर्प्राप्ति में सहायता करने में मदद कर सकता है। ये कॉल बाइंडर प्रॉक्सी ऑब्जेक्ट पर करें।

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

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

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

जावा बैकएंड में:

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

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

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

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

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

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

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

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

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

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

जावा बैकएंड में:

    @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;

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

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

गतिशील रूप से इंटरफ़ेस डिस्क्रिप्टर प्राप्त करना

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

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

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

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

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

एनडीके और रस्ट बैकएंड इस कार्यक्षमता का समर्थन नहीं करते हैं।

स्थिर रूप से इंटरफ़ेस डिस्क्रिप्टर प्राप्त करना

कभी-कभी (जैसे @VintfStability सेवाओं को पंजीकृत करते समय), आपको यह जानना आवश्यक है कि इंटरफ़ेस डिस्क्रिप्टर सांख्यिकीय रूप से क्या है। जावा में, आप निम्न जैसे कोड जोड़कर डिस्क्रिप्टर प्राप्त कर सकते हैं:

    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

रस्ट बैकएंड में:

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

एनम रेंज

मूल बैकएंड में, आप उन संभावित मानों पर पुनरावृति कर सकते हैं जो एक एनम ले सकता है। कोड आकार संबंधी विचारों के कारण, यह वर्तमान में जावा में समर्थित नहीं है।

AIDL में परिभाषित एक एनम MyEnum के लिए, पुनरावृत्ति निम्नानुसार प्रदान की गई है।

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

    ::android::enum_range<MyEnum>()

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

   ::ndk::enum_range<MyEnum>()

रस्ट बैकएंड में:

    MyEnum::enum_values()

धागा प्रबंधन

एक प्रक्रिया में libbinder का प्रत्येक उदाहरण एक थ्रेडपूल बनाए रखता है। अधिकांश उपयोग के मामलों के लिए, यह बिल्कुल एक थ्रेडपूल होना चाहिए, जो सभी बैकएंड पर साझा किया जाए। इसका एकमात्र अपवाद तब होता है जब विक्रेता कोड /dev/vndbinder से बात करने के लिए libbinder की एक और प्रति लोड कर सकता है। चूँकि यह एक अलग बाइंडर नोड पर है, थ्रेडपूल साझा नहीं किया गया है।

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

    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();

रस्ट बैकएंड में:

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

एसिंक रस्ट बैकएंड के साथ, आपको दो थ्रेडपूल की आवश्यकता है: बाइंडर और टोकियो। इसका मतलब यह है कि एसिंक रस्ट का उपयोग करने वाले अनुप्रयोगों पर विशेष विचार करने की आवश्यकता है, खासकर जब बात join_thread_pool के उपयोग की आती है। इस पर अधिक जानकारी के लिए सेवाओं का पंजीकरण अनुभाग देखें।

आरक्षित नाम

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

हम अनुशंसा करते हैं कि आप अपनी एआईडीएल परिभाषाओं में आरक्षित नामों का उपयोग करने से बचें, जहां संभव हो अनर्गोनोमिक बाइंडिंग या पूर्ण संकलन विफलता से बचें।

यदि आपकी एआईडीएल परिभाषाओं में पहले से ही आरक्षित नाम हैं, तो आप प्रोटोकॉल संगत रहते हुए सुरक्षित रूप से फ़ील्ड का नाम बदल सकते हैं; निर्माण जारी रखने के लिए आपको अपना कोड अपडेट करने की आवश्यकता हो सकती है, लेकिन पहले से निर्मित कोई भी प्रोग्राम इंटरऑपरेट करना जारी रखेगा।

जिन नामों से बचना चाहिए: * C++ कीवर्ड * जावा कीवर्ड * रस्ट कीवर्ड