सेवाएं और डेटा ट्रांसफ़र

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

सेवाओं को पंजीकृत करें

HIDL इंटरफ़ेस सर्वर (इंटरफ़ेस लागू करने वाले ऑब्जेक्ट), नाम वाली सेवाओं के तौर पर रजिस्टर किए जा सकते हैं. रजिस्टर किए गए नाम का इंटरफ़ेस या पैकेज के नाम से मेल खाना ज़रूरी नहीं है. अगर कोई नाम नहीं दिया गया है, तो "डिफ़ॉल्ट" नाम का इस्तेमाल किया जाता है. इसका इस्तेमाल उन एचएएल के लिए किया जाना चाहिए जिन्हें एक ही इंटरफ़ेस के दो वर्शन रजिस्टर करने की ज़रूरत नहीं है. उदाहरण के लिए, हर इंटरफ़ेस में सेवा रजिस्ट्रेशन के लिए तय किया गया C++ कॉल यह है:

status_t status = myFoo->registerAsService();
status_t anotherStatus = anotherFoo->registerAsService("another_foo_service");  // if needed

HIDL इंटरफ़ेस का वर्शन, इंटरफ़ेस में ही शामिल होता है. यह सेवा रजिस्ट्रेशन से अपने-आप जुड़ जाता है और हर HIDL इंटरफ़ेस पर, किसी विधि कॉल (android::hardware::IInterface::getInterfaceVersion()) के ज़रिए इसे वापस पाया जा सकता है. सर्वर ऑब्जेक्ट को रजिस्टर करने की ज़रूरत नहीं होती. साथ ही, इन्हें HIDL मेथड पैरामीटर के ज़रिए, किसी ऐसी दूसरी प्रोसेस को पास किया जा सकता है जो सर्वर में HIDL मेथड कॉल करती है.

सेवाएं खोजना

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

// C++
sp<V1_1::IFooService> service = V1_1::IFooService::getService();
sp<V1_1::IFooService> alternateService = V1_1::IFooService::getService("another_foo_service");
// Java
V1_1.IFooService service = V1_1.IFooService.getService(true /* retry */);
V1_1.IFooService alternateService = V1_1.IFooService.getService("another", true /* retry */);

HIDL इंटरफ़ेस के हर वर्शन को एक अलग इंटरफ़ेस माना जाता है. इसलिए, IFooService वर्शन 1.1 और IFooService वर्शन 2.2, दोनों को "foo_service" के तौर पर रजिस्टर किया जा सकता है. साथ ही, getService("foo_service") को किसी भी इंटरफ़ेस पर, उस इंटरफ़ेस के लिए रजिस्टर की गई सेवा मिलती है. इसलिए, ज़्यादातर मामलों में रजिस्टर करने या खोजने के लिए, नाम पैरामीटर की ज़रूरत नहीं होती है. इसका मतलब है कि नाम "डिफ़ॉल्ट" है.

वेंडर इंटरफ़ेस ऑब्जेक्ट, दिखाए गए इंटरफ़ेस के ट्रांसपोर्ट तरीके में भी भूमिका निभाता है. पैकेज android.hardware.foo@1.0 में मौजूद इंटरफ़ेस IFoo के लिए, IFoo::getService से मिला इंटरफ़ेस हमेशा डिवाइस मेनिफ़ेस्ट में android.hardware.foo के लिए बताए गए ट्रांसपोर्ट तरीके का इस्तेमाल करता है. ऐसा तब होता है, जब एंट्री मौजूद हो. अगर ट्रांसपोर्ट का तरीका उपलब्ध नहीं है, तो nullptr दिखाया जाता है.

कुछ मामलों में, सेवा मिलने से पहले ही इसे जारी रखना ज़रूरी हो सकता है. उदाहरण के लिए, ऐसा तब हो सकता है, जब कोई क्लाइंट खुद सेवा की सूचनाएं मैनेज करना चाहता हो या किसी डाइग्नोस्टिक्स प्रोग्राम (जैसे कि atrace) में, सभी हार्डवेयर सेवाएं पाने और उन्हें वापस पाने की ज़रूरत हो. इस मामले में, ज़्यादा एपीआई उपलब्ध कराए जाते हैं. जैसे, C++ में tryGetService या Java में getService("instance-name", false). Java में दिए गए लेगसी एपीआईgetService का इस्तेमाल भी सेवा की सूचनाओं के साथ किया जाना चाहिए. इस एपीआई का इस्तेमाल करने से, रेस कंडीशन से बचा नहीं जा सकता. रेस कंडीशन तब होती है, जब क्लाइंट इनमें से किसी एक एपीआई के साथ अनुरोध करने के बाद, सर्वर खुद को रजिस्टर कर लेता है.

सेवा बंद होने की सूचनाएं

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

  1. HIDL क्लास/इंटरफ़ेस hidl_death_recipient का सबक्लास बनाएं (C++ कोड में, न कि HIDL में).
  2. इसके serviceDied() तरीके को बदलें.
  3. hidl_death_recipient सबक्लास के किसी ऑब्जेक्ट को इंस्टैंशिएट करें.
  4. IDeathRecipient के इंटरफ़ेस ऑब्जेक्ट को मॉनिटर करने के लिए, सेवा पर linkToDeath() तरीके को कॉल करें. ध्यान दें कि इस तरीके से, मृत व्यक्ति के खाते का मालिकाना हक पाने वाले व्यक्ति या उस प्रॉक्सी का मालिकाना हक नहीं मिलता जिस पर इसे कॉल किया जाता है.

सूडोकोड का उदाहरण (C++ और Java मिलते-जुलते हैं):

class IMyDeathReceiver : hidl_death_recipient {
  virtual void serviceDied(uint64_t cookie,
                           wp<IBase>& service) override {
    log("RIP service %d!", cookie);  // Cookie should be 42
  }
};
....
IMyDeathReceiver deathReceiver = new IMyDeathReceiver();
m_importantService->linkToDeath(deathReceiver, 42);

एक ही व्यक्ति को, कई अलग-अलग सेवाओं के लिए डेथ रिसीवर के तौर पर रजिस्टर किया जा सकता है.

डेटा ट्रांसफ़र

.hal फ़ाइलों में इंटरफ़ेस में बताए गए तरीकों को कॉल करके, डेटा को किसी सेवा पर भेजा जा सकता है. इसके दो तरीके हैं:

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

कोई ऐसा तरीका जो कोई वैल्यू नहीं दिखाता है, लेकिन oneway के तौर पर एलान नहीं किया गया है, वह अब भी ब्लॉक कर रहा है.

HIDL इंटरफ़ेस में बताए गए सभी तरीकों को एक ही दिशा में कॉल किया जाता है. यानी, HAL से या HAL में. इंटरफ़ेस से यह पता नहीं चलता कि इसे किस दिशा में कहा जाता है. जिन आर्किटेक्चर को एचएएल से कॉल शुरू करने की ज़रूरत होती है उन्हें एचएएल पैकेज में दो (या उससे ज़्यादा) इंटरफ़ेस देने चाहिए. साथ ही, हर प्रोसेस से सही इंटरफ़ेस उपलब्ध कराना चाहिए. क्लाइंट और सर्वर शब्दों का इस्तेमाल, इंटरफ़ेस के कॉलिंग डायरेक्शन के हिसाब से किया जाता है. इसका मतलब है कि एचएएल, एक इंटरफ़ेस का सर्वर और दूसरे इंटरफ़ेस का क्लाइंट हो सकता है.

कॉलबैक

कॉलबैक शब्द दो अलग-अलग कॉन्सेप्ट के बारे में बताता है. इन कॉन्सेप्ट को सिंक्रोनस कॉलबैक और असिंक्रोनस कॉलबैक के तौर पर जाना जाता है.

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

एसिंक्रोनस कॉलबैक की मदद से, HIDL इंटरफ़ेस के सर्वर को कॉल शुरू करने की अनुमति मिलती है. ऐसा करने के लिए, पहले इंटरफ़ेस के ज़रिए दूसरे इंटरफ़ेस का एक इंस्टेंस पास किया जाता है. पहले इंटरफ़ेस का क्लाइंट, दूसरे इंटरफ़ेस के सर्वर के तौर पर काम करना चाहिए. पहले इंटरफ़ेस का सर्वर, दूसरे इंटरफ़ेस ऑब्जेक्ट पर मेथड कॉल कर सकता है. उदाहरण के लिए, एचएएल लागू करने पर, उस प्रोसेस को जानकारी भेजी जा सकती है जो उसका इस्तेमाल कर रही है. इसके लिए, उस प्रोसेस से बनाए गए और उसे दिखाए गए इंटरफ़ेस ऑब्जेक्ट पर मेथड को कॉल किया जाता है. असाइनोक्रोनस कॉलबैक के लिए इस्तेमाल किए गए इंटरफ़ेस के तरीके, ब्लॉक कर सकते हैं (और कॉल करने वाले को वैल्यू दिखा सकते हैं) या oneway हो सकते हैं. उदाहरण के लिए, HIDL C++ में "असाइनोक्रोनस कॉलबैक" देखें.

मेमोरी के मालिकाना हक को आसान बनाने के लिए, मेथड कॉल और कॉलबैक में सिर्फ़ in पैरामीटर इस्तेमाल किए जाते हैं. इनमें out या inout पैरामीटर इस्तेमाल नहीं किए जा सकते.

हर लेन-देन की सीमा

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

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

लागू करने के तरीके

HIDL, टारगेट भाषा (C++ या Java) में ज़रूरी टाइप, तरीकों, और कॉलबैक का एलान करने वाली हेडर फ़ाइलें जनरेट करता है. HIDL के ज़रिए तय किए गए तरीकों और कॉलबैक का प्रोटोटाइप, क्लाइंट और सर्वर कोड, दोनों के लिए एक जैसा होता है. HIDL सिस्टम, कॉलर साइड पर तरीकों के प्रॉक्सी लागू करता है. ये तरीकों को आईपीसी ट्रांसपोर्ट के लिए डेटा व्यवस्थित करते हैं. साथ ही, कॉली साइड पर स्टब कोड लागू करते हैं. यह कोड, डेटा को तरीकों के डेवलपर लागू करने में मदद करता है.

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

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

आरपीसी के अलावा अन्य तरीकों से डेटा ट्रांसफ़र करना

आरपीसी कॉल का इस्तेमाल किए बिना डेटा ट्रांसफ़र करने के लिए, HIDL में दो तरीके हैं: शेयर की गई स्मृति और फ़ास्ट मैसेज क्यू (एफ़एमक्यू). ये दोनों तरीके सिर्फ़ C++ में काम करते हैं.

  • शेयर की गई मेमोरी. डिफ़ॉल्ट रूप से मौजूद HIDL टाइप memory का इस्तेमाल, एलोकेट की गई शेयर की गई मेमोरी को दिखाने वाले ऑब्जेक्ट को पास करने के लिए किया जाता है. शेयर की गई मेमोरी को मैप करने के लिए, रिसीविंग प्रोसेस में इसका इस्तेमाल किया जा सकता है.
  • फ़ास्ट मैसेज क्यू (FMQ). HIDL, टेंप्लेट वाला मैसेज लाइन टाइप उपलब्ध कराता है, जो बिना इंतज़ार किए मैसेज भेजने की सुविधा देता है. यह पासथ्रू या बाइंडर मोड में, कर्नेल या शेड्यूलर का इस्तेमाल नहीं करता. इंटर-डिवाइस कम्यूनिकेशन में ये प्रॉपर्टी नहीं होती हैं. आम तौर पर, HAL अपनी लाइन के आखिर में ऑब्जेक्ट सेट अप करता है. यह ऐसा ऑब्जेक्ट होता है जिसे पहले से मौजूद HIDL टाइप MQDescriptorSync या MQDescriptorUnsync के पैरामीटर की मदद से, आरपीसी के ज़रिए पास किया जा सकता है. इस ऑब्जेक्ट का इस्तेमाल, लाइन के दूसरे छोर को सेट अप करने के लिए, पाने वाली प्रोसेस कर सकती है.
    • सिंक की सूचियों में आइटम की संख्या ज़्यादा नहीं हो सकती. साथ ही, इनमें सिर्फ़ एक रीडर हो सकता है.
    • अनसिंक की सूचियों में डेटा का ओवरफ़्लो हो सकता है. साथ ही, इनमें कई रीडर हो सकते हैं. इनमें से हर रीडर को समय पर डेटा पढ़ना होगा, नहीं तो वह डेटा मिट जाएगा.
    किसी भी टाइप के लिए, डेटा की संख्या कम होने की अनुमति नहीं है. खाली कतार से डेटा पढ़ने की सुविधा काम नहीं करती. साथ ही, हर टाइप के लिए सिर्फ़ एक लेखक हो सकता है.

एफ़एमक्यू के बारे में ज़्यादा जानकारी के लिए, फ़ास्ट मैसेज क्यू (एफ़एमक्यू) देखें.