सेवाएँ एवं amp; डेटा स्थानांतरण

यह अनुभाग वर्णन करता है कि सेवाओं को कैसे पंजीकृत और खोजा जाए और .hal फ़ाइलों में इंटरफेस में परिभाषित तरीकों को कॉल करके किसी सेवा में डेटा कैसे भेजा जाए।

पंजीकरण सेवाएँ

HIDL इंटरफ़ेस सर्वर (इंटरफ़ेस को लागू करने वाली वस्तुएं) को नामित सेवाओं के रूप में पंजीकृत किया जा सकता है। पंजीकृत नाम का इंटरफ़ेस या पैकेज नाम से संबंधित होना आवश्यक नहीं है। यदि कोई नाम निर्दिष्ट नहीं है, तो "डिफ़ॉल्ट" नाम का उपयोग किया जाता है; इसका उपयोग एचएएल के लिए किया जाना चाहिए जिन्हें एक ही इंटरफ़ेस के दो कार्यान्वयन पंजीकृत करने की आवश्यकता नहीं है। उदाहरण के लिए, प्रत्येक इंटरफ़ेस में परिभाषित सेवा पंजीकरण के लिए C++ कॉल है:

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

HIDL इंटरफ़ेस का संस्करण इंटरफ़ेस में ही शामिल है। यह स्वचालित रूप से सेवा पंजीकरण से जुड़ा हुआ है और प्रत्येक HIDL इंटरफ़ेस पर एक विधि कॉल ( android::hardware::IInterface::getInterfaceVersion() ) के माध्यम से पुनर्प्राप्त किया जा सकता है। सर्वर ऑब्जेक्ट को पंजीकृत करने की आवश्यकता नहीं है और इसे एचआईडीएल विधि पैरामीटर के माध्यम से किसी अन्य प्रक्रिया में पारित किया जा सकता है जो सर्वर में एचआईडीएल विधि कॉल करेगा।

सेवाओं की खोज

क्लाइंट कोड द्वारा अनुरोध किसी दिए गए इंटरफ़ेस के लिए नाम और संस्करण द्वारा किए जाते हैं, वांछित एचएएल क्लास पर 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") उस इंटरफ़ेस के लिए पंजीकृत सेवा मिलती है। यही कारण है कि, ज्यादातर मामलों में, पंजीकरण या खोज के लिए कोई नाम पैरामीटर प्रदान करने की आवश्यकता नहीं होती है (जिसका अर्थ है नाम "डिफ़ॉल्ट")।

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

कुछ मामलों में, सेवा प्राप्त किए बिना भी तुरंत जारी रखना आवश्यक हो सकता है। ऐसा तब हो सकता है (उदाहरण के लिए) जब कोई ग्राहक सेवा सूचनाओं को स्वयं या डायग्नोस्टिक प्रोग्राम (जैसे कि atrace ) में प्रबंधित करना चाहता है, जिसे सभी hwservices प्राप्त करने और उन्हें पुनर्प्राप्त करने की आवश्यकता होती है। इस मामले में, अतिरिक्त एपीआई प्रदान की जाती हैं जैसे C++ में tryGetService या Java में getService("instance-name", false) जावा में प्रदान की गई लीगेसी एपीआई 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 विधियों में किया जाता है जो डेटा लौटाते हैं। एक HIDL विधि जो एक से अधिक मान लौटाती है (या गैर-आदिम प्रकार का एक मान लौटाती है) कॉलबैक फ़ंक्शन के माध्यम से अपने परिणाम लौटाती है। यदि केवल एक मान लौटाया जाता है और यह एक आदिम प्रकार है, तो कॉलबैक का उपयोग नहीं किया जाता है और मान विधि से वापस किया जाता है। सर्वर HIDL विधियों को लागू करता है और क्लाइंट कॉलबैक को लागू करता है।

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

मेमोरी स्वामित्व को सरल बनाने के लिए, विधि कॉल और कॉलबैक केवल पैरामीटर in हैं और out या inout पैरामीटर का समर्थन नहीं करते हैं।

प्रति लेनदेन सीमा

एचआईडीएल विधियों और कॉलबैक में भेजे गए डेटा की मात्रा पर प्रति-लेन-देन सीमाएं नहीं लगाई जाती हैं। हालाँकि, प्रति लेनदेन 4KB से अधिक कॉल को अत्यधिक माना जाता है। यदि यह देखा जाता है, तो दिए गए HIDL इंटरफ़ेस को पुनः आर्किटेक्चर करने की अनुशंसा की जाती है। एक और सीमा एक साथ कई लेनदेन को संभालने के लिए एचआईडीएल बुनियादी ढांचे के लिए उपलब्ध संसाधन है। एक प्रक्रिया में कॉल भेजने वाले कई थ्रेड्स या प्रक्रियाओं या कई oneway कॉलों के कारण कई लेन-देन एक साथ उड़ान में हो सकते हैं, जिन्हें प्राप्त करने वाली प्रक्रिया द्वारा जल्दी से नियंत्रित नहीं किया जाता है। सभी समवर्ती लेनदेन के लिए उपलब्ध अधिकतम कुल स्थान डिफ़ॉल्ट रूप से 1MB है।

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

विधि कार्यान्वयन

HIDL लक्ष्य भाषा (C++ या Java) में आवश्यक प्रकारों, विधियों और कॉलबैक की घोषणा करते हुए हेडर फ़ाइलें उत्पन्न करता है। एचआईडीएल-परिभाषित विधियों और कॉलबैक का प्रोटोटाइप क्लाइंट और सर्वर कोड दोनों के लिए समान है। एचआईडीएल प्रणाली कॉलर पक्ष पर विधियों का प्रॉक्सी कार्यान्वयन प्रदान करती है जो आईपीसी परिवहन के लिए डेटा को व्यवस्थित करती है, और कैली पक्ष पर स्टब कोड प्रदान करती है जो डेटा को विधियों के डेवलपर कार्यान्वयन में भेजती है।

किसी फ़ंक्शन (HIDL विधि या कॉलबैक) के कॉलर के पास फ़ंक्शन में पारित डेटा संरचनाओं का स्वामित्व होता है, और कॉल के बाद स्वामित्व बरकरार रखता है; सभी मामलों में प्राप्तकर्ता को भंडारण खाली करने या जारी करने की आवश्यकता नहीं है।

  • C++ में, डेटा केवल-पढ़ने के लिए हो सकता है (इसे लिखने का प्रयास विभाजन दोष का कारण बन सकता है) और कॉल की अवधि के लिए मान्य है। क्लाइंट डेटा को कॉल से परे प्रचारित करने के लिए उसकी डीप-कॉपी कर सकता है।
  • जावा में, कोड को डेटा की एक स्थानीय प्रतिलिपि (एक सामान्य जावा ऑब्जेक्ट) प्राप्त होती है, जिसे वह रख सकता है और संशोधित कर सकता है या कचरा एकत्र करने की अनुमति दे सकता है।

गैर-आरपीसी डेटा स्थानांतरण

HIDL के पास RPC कॉल का उपयोग किए बिना डेटा स्थानांतरित करने के दो तरीके हैं: साझा मेमोरी और एक फास्ट मैसेज क्यू (FMQ), दोनों केवल C++ में समर्थित हैं।

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

एफएमक्यू पर अधिक जानकारी के लिए फास्ट मैसेज क्यू (एफएमक्यू) देखें।