इंटरफ़ेस का वर्शन

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

HIDL कोड का स्ट्रक्चर

HIDL कोड को व्यवस्थित, उपयोगकर्ता के तय किए गए टाइप, इंटरफ़ेस, और पैकेज में किया जाता है:

  • उपयोगकर्ता के तय किए गए टाइप (UDTs). HIDL, प्राइमटिव डेटा टाइप के एक सेट का ऐक्सेस देता है. इसका इस्तेमाल, स्ट्रक्चर, यूनियन, और एनोटेशन के ज़रिए ज़्यादा जटिल टाइप बनाने के लिए किया जा सकता है. यूडीटी, इंटरफ़ेस के तरीकों में पास किए जाते हैं. इन्हें पैकेज के लेवल पर (सभी इंटरफ़ेस के लिए सामान्य) या किसी इंटरफ़ेस के लिए स्थानीय तौर पर तय किया जा सकता है.
  • इंटरफ़ेस. HIDL के बुनियादी बिल्डिंग ब्लॉक के तौर पर, इंटरफ़ेस में यूडीटी और तरीकों के एलान होते हैं. इंटरफ़ेस, किसी दूसरे इंटरफ़ेस से भी इनहेरिट किए जा सकते हैं.
  • पैकेज. मिलते-जुलते HIDL इंटरफ़ेस और उन पर काम करने वाले डेटा टाइप को व्यवस्थित करता है. पैकेज की पहचान नाम और वर्शन से की जाती है. इसमें ये चीज़ें शामिल होती हैं:
    • डेटा टाइप की परिभाषा वाली फ़ाइल, जिसे types.hal कहा जाता है.
    • शून्य या उससे ज़्यादा इंटरफ़ेस, हर एक अपनी .hal फ़ाइल में.

डेटा टाइप की परिभाषा वाली फ़ाइल types.hal में सिर्फ़ यूडीटी होते हैं. सभी पैकेज-लेवल यूडीटी को एक ही फ़ाइल में रखा जाता है. टारगेट भाषा में मौजूद जानकारी, पैकेज के सभी इंटरफ़ेस के लिए उपलब्ध होती है.

वर्शनिंग का फ़िलॉसफ़ी

किसी वर्शन (जैसे, 1.0) के लिए पब्लिश किए जाने के बाद, android.hardware.nfc जैसे HIDL पैकेज में बदलाव नहीं किया जा सकता. पैकेज के इंटरफ़ेस में बदलाव या उसके यूडीटी में कोई बदलाव, सिर्फ़ किसी दूसरे पैकेज में किया जा सकता है.

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

कॉन्सेप्ट के हिसाब से, कोई पैकेज कई तरीकों से किसी दूसरे पैकेज से जुड़ा हो सकता है:

  • बिलकुल नहीं.
  • पैकेज-लेवल पर, पुराने सिस्टम के साथ काम करने की सुविधा. ऐसा, किसी पैकेज के नए माइनर वर्शन के अपग्रेड (बढ़ाए गए अगले वर्शन) के लिए होता है. नए पैकेज का नाम और मेजर वर्शन, पुराने पैकेज के जैसा ही होता है, लेकिन माइनर वर्शन ज़्यादा होता है. फ़ंक्शन के हिसाब से, नया पैकेज पुराने पैकेज का सुपरसेट है. इसका मतलब है कि:
    • पैरंट पैकेज के टॉप-लेवल इंटरफ़ेस, नए पैकेज में मौजूद होते हैं. हालांकि, इंटरफ़ेस में नए तरीके, इंटरफ़ेस-लेवल एक्सटेंशन (यह इंटरफ़ेस-लेवल एक्सटेंशन नीचे बताया गया है), और types.hal में नए यूडीटी हो सकते हैं.
    • नए पैकेज में नए इंटरफ़ेस भी जोड़े जा सकते हैं.
    • पैरंट पैकेज के सभी डेटा टाइप, नए पैकेज में मौजूद होते हैं और इन्हें पुराने पैकेज के तरीकों से मैनेज किया जा सकता है. हालांकि, हो सकता है कि इन तरीकों को फिर से लागू किया गया हो.
    • नए डेटा टाइप को, अपग्रेड किए गए मौजूदा इंटरफ़ेस के नए तरीकों या नए इंटरफ़ेस के ज़रिए इस्तेमाल करने के लिए भी जोड़ा जा सकता है.
  • इंटरफ़ेस-लेवल पर, पुराने सिस्टम के साथ काम करने की सुविधा. नया पैकेज, ओरिजनल पैकेज को भी बड़ा कर सकता है. इसके लिए, अलग-अलग इंटरफ़ेस जोड़े जाते हैं, जो मुख्य फ़ंक्शन के बजाय सिर्फ़ अतिरिक्त फ़ंक्शन देते हैं. इस काम के लिए, ये काम किए जा सकते हैं:
    • नए पैकेज के इंटरफ़ेस को पुराने पैकेज के डेटा टाइप का इस्तेमाल करना होगा.
    • नए पैकेज में मौजूद इंटरफ़ेस, एक या एक से ज़्यादा पुराने पैकेज के इंटरफ़ेस को एक्सटेंड कर सकते हैं.
  • ओरिजनल वर्शन को पुराने सिस्टम के साथ काम करने की सुविधा दें. यह पैकेज का मुख्य वर्शन है. इन दोनों के बीच कोई संबंध नहीं होना चाहिए. अगर कोई अंतर है, तो इसे पैकेज के पुराने वर्शन के टाइप और पुराने पैकेज के इंटरफ़ेस के सबसेट के इनहेरिटेंस के कॉम्बिनेशन से दिखाया जा सकता है.

इंटरफ़ेस को व्यवस्थित करना

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

Treble, वेंडर और सिस्टम के अलग-अलग कॉम्पोनेंट के साथ काम करता है. इसमें, किसी डिवाइस पर vendor.img और system.img को अलग-अलग कॉम्पाइल किया जा सकता है. vendor.img और system.img के बीच के सभी इंटरैक्शन को साफ़ तौर पर और पूरी तरह से तय किया जाना चाहिए, ताकि वे कई सालों तक काम करते रहें. इसमें कई एपीआई प्लैटफ़ॉर्म शामिल हैं. हालांकि, एक मुख्य प्लैटफ़ॉर्म, आईपीसी (इंटरप्रोसेस कम्यूनिकेशन) मकैनिज्म है. इसका इस्तेमाल, HIDL system.img/vendor.img बाउंड्री पर इंटरप्रोसेस कम्यूनिकेशन के लिए किया जाता है.

ज़रूरी शर्तें

HIDL के ज़रिए पास किया जाने वाला सारा डेटा साफ़ तौर पर तय होना चाहिए. यह पक्का करने के लिए कि अलग-अलग कॉम्पाइल किए जाने या अलग-अलग डेवलप किए जाने पर भी, लागू करने और क्लाइंट एक साथ काम कर सकें, डेटा को इन ज़रूरी शर्तों का पालन करना होगा:

  • इनका ब्यौरा सीधे HIDL में दिया जा सकता है. इसके लिए, स्ट्रक्चर एनम, वगैरह का इस्तेमाल किया जा सकता है. साथ ही, इनका नाम और मतलब भी दिया जा सकता है.
  • इसे ISO/IEC 7816 जैसे किसी सार्वजनिक स्टैंडर्ड के ज़रिए बताया जा सकता है.
  • इसे हार्डवेयर स्टैंडर्ड या हार्डवेयर के फ़िज़िकल लेआउट के हिसाब से बताया जा सकता है.
  • ज़रूरत पड़ने पर, यह डेटा साफ़ तौर पर न दिखने वाला हो सकता है. जैसे, सार्वजनिक कुंजियां, आईडी वगैरह.

अगर ओपेक डेटा का इस्तेमाल किया जाता है, तो उसे सिर्फ़ HIDL इंटरफ़ेस के एक तरफ़ से पढ़ा जाना चाहिए. उदाहरण के लिए, अगर vendor.img कोड, system.img पर किसी कॉम्पोनेंट को कोई स्ट्रिंग मैसेज या vec<uint8_t> डेटा देता है, तो उस डेटा को system.img खुद पार्स नहीं कर सकता. उसे सिर्फ़ vendor.img को भेजा जा सकता है, ताकि वह उसका विश्लेषण कर सके. vendor.img से vendor.img या किसी दूसरे डिवाइस पर वेंडर कोड में वैल्यू भेजते समय, डेटा के फ़ॉर्मैट और उसे समझने के तरीके के बारे में सटीक जानकारी देना ज़रूरी है. यह जानकारी अब भी इंटरफ़ेस का हिस्सा है.system.img

दिशा-निर्देश

आपके पास सिर्फ़ .hal फ़ाइलों का इस्तेमाल करके, एचएएल का लागू करने या क्लाइंट लिखने का विकल्प होना चाहिए. इसका मतलब है कि आपको Android सोर्स या सार्वजनिक स्टैंडर्ड देखने की ज़रूरत नहीं होगी. हमारा सुझाव है कि आप ज़रूरी व्यवहार के बारे में सटीक जानकारी दें. "लागू करने से A या B हो सकता है" जैसे स्टेटमेंट, लागू करने की प्रोसेस को उन क्लाइंट के साथ जोड़ने के लिए बढ़ावा देते हैं जिनके लिए उन्हें डेवलप किया गया है.

HIDL कोड लेआउट

HIDL में कोर और वेंडर पैकेज शामिल होते हैं.

Google ने मुख्य HIDL इंटरफ़ेस तय किए हैं. ये पैकेज android.hardware. से शुरू होते हैं और इनका नाम सबसिस्टम के हिसाब से रखा जाता है. इनमें नेस्ट किए गए लेवल भी हो सकते हैं. उदाहरण के लिए, एनएफ़सी पैकेज का नाम android.hardware.nfc और कैमरा पैकेज का नाम android.hardware.camera है. आम तौर पर, कोर पैकेज का नाम android.hardware.[name1].[name2]…. होता है. HIDL पैकेज के नाम के साथ-साथ उनका वर्शन भी होता है. उदाहरण के लिए, पैकेज android.hardware.camera का वर्शन 3.4 हो सकता है. यह जानकारी ज़रूरी है, क्योंकि पैकेज के वर्शन से सोर्स ट्री में उसके प्लेसमेंट पर असर पड़ता है.

सभी मुख्य पैकेज, बिल्ड सिस्टम में hardware/interfaces/ में रखे जाते हैं. पैकेज android.hardware.[name1].[name2]… के वर्शन $m.$n में, hardware/interfaces/name1/name2//$m.$n/ में मौजूद है; पैकेज android.hardware.camera के वर्शन 3.4, डायरेक्ट्री hardware/interfaces/camera/3.4/. में मौजूद है. पैकेज के प्रीफ़िक्स android.hardware. और पाथ hardware/interfaces/ के बीच, हार्ड कोड की गई मैपिंग मौजूद है.

नॉन-कोर (वेंडर) पैकेज, SoC वेंडर या ओडीएम बनाते हैं. नॉन-कोर पैकेज के लिए प्रीफ़िक्स vendor.$(VENDOR).hardware. है. इसमें $(VENDOR), SoC वेंडर या OEM/ODM का मतलब है. यह ट्री में पाथ vendor/$(VENDOR)/interfaces पर मैप होता है. यह मैपिंग भी हार्ड-कोड की गई होती है.

उपयोगकर्ता के तय किए गए टाइप के पूरी तरह क्वालिफ़ाइड नाम

HIDL में, हर यूडीटी का पूरा नाम होता है. इसमें यूडीटी का नाम, पैकेज का नाम, और पैकेज का वर्शन शामिल होता है. पूरी तरह से क्वालिफ़ाइड नाम का इस्तेमाल सिर्फ़ तब किया जाता है, जब टाइप के इंस्टेंस तय किए जाते हैं. उदाहरण के लिए, मान लें कि पैकेज android.hardware.nfc, के वर्शन 1.0 में NfcData नाम का स्ट्रक्चर तय किया गया है. एलान की साइट पर (types.hal में या इंटरफ़ेस के एलान में), एलान में यह जानकारी दी जाती है:

struct NfcData {
    vec<uint8_t> data;
};

इस टाइप के किसी इंस्टेंस का एलान करते समय (चाहे डेटा स्ट्रक्चर में हो या विधि पैरामीटर के तौर पर), टाइप के पूरी तरह से सही नाम का इस्तेमाल करें:

android.hardware.nfc@1.0::NfcData

इसका सामान्य सिंटैक्स PACKAGE@VERSION::UDT है, जहां:

  • PACKAGE, बिंदु से अलग किए गए किसी HIDL पैकेज का नाम होता है (उदाहरण के लिए, android.hardware.nfc).
  • VERSION, पैकेज का बिंदु से अलग किया गया major.minor-version फ़ॉर्मैट है (उदाहरण के लिए, 1.0).
  • UDT, बिंदु से अलग किए गए किसी HIDL UDT का नाम है. HIDL, नेस्ट किए गए यूडीटी के साथ काम करता है और HIDL इंटरफ़ेस में यूडीटी (नेस्ट किए गए एलान का एक टाइप) हो सकते हैं. इसलिए, नामों को ऐक्सेस करने के लिए बिंदु का इस्तेमाल किया जाता है.

उदाहरण के लिए, अगर पैकेज android.hardware.example के वर्शन 1.0 में, सामान्य टाइप वाली फ़ाइल में नेस्ट किया गया यह एलान किया गया था:

// types.hal
package android.hardware.example@1.0;
struct Foo {
    struct Bar {
        // …
    };
    Bar cheers;
};

Bar का पूरा नाम android.hardware.example@1.0::Foo.Bar है. अगर नेस्ट किया गया एलान, ऊपर दिए गए पैकेज के अलावा, IQuux नाम के इंटरफ़ेस में भी है, तो:

// IQuux.hal
package android.hardware.example@1.0;
interface IQuux {
    struct Foo {
        struct Bar {
            // …
        };
        Bar cheers;
    };
    doSomething(Foo f) generates (Foo.Bar fb);
};

Bar का पूरा नाम android.hardware.example@1.0::IQuux.Foo.Bar है.

दोनों मामलों में, Bar को Bar के तौर पर सिर्फ़ Foo के एलान के दायरे में ही दिखाया जा सकता है. पैकेज या इंटरफ़ेस लेवल पर, आपको Bar को Foo:Foo.Bar के ज़रिए रेफ़र करना होगा. जैसे, ऊपर दिए गए तरीके doSomething के एलान में बताया गया है. इसके अलावा, इस तरीके को ज़्यादा जानकारी के साथ इस तरह से भी बताया जा सकता है:

// IQuux.hal
doSomething(android.hardware.example@1.0::IQuux.Foo f) generates (android.hardware.example@1.0::IQuux.Foo.Bar fb);

पूरी तरह क्वालिफ़ाइड इन्यूमरेशन वैल्यू

अगर कोई यूडीटी, एनम टाइप है, तो एनम टाइप की हर वैल्यू का एक पूरी तरह से क्वालिफ़ाइड नाम होता है. यह नाम, एनम टाइप के पूरी तरह से क्वालिफ़ाइड नाम से शुरू होता है. इसके बाद, कोलन और फिर एनम वैल्यू का नाम होता है. उदाहरण के लिए, मान लें कि पैकेज android.hardware.nfc, के वर्शन 1.0 में, किसी एनम टाइप NfcStatus की जानकारी दी गई है:

enum NfcStatus {
    STATUS_OK,
    STATUS_FAILED
};

STATUS_OK का पूरा नाम यह है:

android.hardware.nfc@1.0::NfcStatus:STATUS_OK

इसका सामान्य सिंटैक्स PACKAGE@VERSION::UDT:VALUE है, जहां:

  • PACKAGE@VERSION::UDT, पूरी तरह क्वालिफ़ाइड नाम है, जो कि एनम टाइप के लिए एक जैसा है.
  • VALUE वैल्यू का नाम है.

अपने-आप अनुमान लगाने वाले नियम

यूडीटी का पूरा नाम बताने की ज़रूरत नहीं है. यूडीटी के नाम में ये चीज़ें शामिल नहीं की जा सकतीं:

  • पैकेज, जैसे कि @1.0::IFoo.Type
  • पैकेज और वर्शन, जैसे कि IFoo.Type

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

पहला नियम

अगर कोई पैकेज और वर्शन नहीं दिया गया है, तो स्थानीय नाम का लुकअप करने की कोशिश की जाती है. उदाहरण:

interface Nfc {
    typedef string NfcErrorMessage;
    send(NfcData d) generates (@1.0::NfcStatus s, NfcErrorMessage m);
};

NfcErrorMessage को स्थानीय तौर पर खोजा जाता है और इसके ऊपर मौजूद typedef का पता चलता है. NfcData को स्थानीय तौर पर भी खोजा जाता है. हालांकि, इसे स्थानीय तौर पर तय नहीं किया गया है. इसलिए, नियम 2 और 3 का इस्तेमाल किया जाता है. @1.0::NfcStatus एक वर्शन उपलब्ध कराता है, इसलिए पहला नियम लागू नहीं होता.

दूसरा नियम

अगर पहला नियम लागू नहीं होता है और पूरी तरह से सही नाम का कोई कॉम्पोनेंट (पैकेज, वर्शन या पैकेज और वर्शन) मौजूद नहीं है, तो कॉम्पोनेंट को मौजूदा पैकेज की जानकारी से अपने-आप भरा जाता है. इसके बाद, HIDL कंपाइलर मौजूदा फ़ाइल (और सभी इंपोर्ट) में, अपने-आप भरी गई पूरी जानकारी वाला नाम ढूंढता है. ऊपर दिए गए उदाहरण का इस्तेमाल करके, मान लें कि ExtendedNfcData के एलान को NfcData के उसी पैकेज (android.hardware.nfc) और वर्शन (1.0) में किया गया था, जैसा कि यहां बताया गया है:

struct ExtendedNfcData {
    NfcData base;
    // … additional members
};

HIDL कंपाइलर, मौजूदा पैकेज से पैकेज का नाम और वर्शन का नाम भरता है, ताकि पूरी तरह से क्वालिफ़ाइड UDT का नाम android.hardware.nfc@1.0::NfcData जनरेट किया जा सके. अगर नाम मौजूदा पैकेज में मौजूद है (यह मानते हुए कि इसे सही तरीके से इंपोर्ट किया गया है), तो इसका इस्तेमाल एलान के लिए किया जाता है.

मौजूदा पैकेज में मौजूद नाम सिर्फ़ तब इंपोर्ट किया जाता है, जब इनमें से कोई एक बात सही हो:

  • इसे import स्टेटमेंट के साथ साफ़ तौर पर इंपोर्ट किया जाता है.
  • इसे मौजूदा पैकेज में types.hal में बताया गया है

अगर NfcData को सिर्फ़ वर्शन नंबर के आधार पर मंज़ूरी मिली है, तो भी यही प्रक्रिया अपनाई जाती है:

struct ExtendedNfcData {
    // autofill the current package name (android.hardware.nfc)
    @1.0::NfcData base;
    // … additional members
};

तीसरा नियम

अगर नियम 2 के तहत कोई मैच नहीं मिलता है (मौजूदा पैकेज में UDT तय नहीं किया गया है), तो HIDL कंपाइलर, इंपोर्ट किए गए सभी पैकेज में मैच खोजता है. ऊपर दिए गए उदाहरण का इस्तेमाल करके, मान लें कि ExtendedNfcData को पैकेज android.hardware.nfc के वर्शन 1.1 में डिक्लेयर्ड किया गया है, 1.1 1.0 को सही तरीके से इंपोर्ट करता है (पैकेज-लेवल एक्सटेंशन देखें), और परिभाषा में सिर्फ़ यूडीटी का नाम बताया गया है:

struct ExtendedNfcData {
    NfcData base;
    // … additional members
};

कंपाइलर, NfcData नाम का कोई यूडीटी खोजता है और उसे android.hardware.nfc में वर्शन 1.0 पर एक यूडीटी मिलता है. इस वजह से, android.hardware.nfc@1.0::NfcData का पूरी तरह से क्वालिफ़ाइड यूडीटी बन जाता है. अगर किसी ऐसे UDT के लिए एक से ज़्यादा मैच मिलते हैं जो कुछ हद तक ज़रूरी शर्तें पूरी करता है, तो HIDL कंपाइलर गड़बड़ी का मैसेज दिखाता है.

उदाहरण

दूसरे नियम का इस्तेमाल करके, मौजूदा पैकेज में तय किए गए इंपोर्ट किए गए टाइप को, किसी दूसरे पैकेज से इंपोर्ट किए गए टाइप के मुकाबले प्राथमिकता दी जाती है:

// hardware/interfaces/foo/1.0/types.hal
package android.hardware.foo@1.0;
struct S {};

// hardware/interfaces/foo/1.0/IFooCallback.hal
package android.hardware.foo@1.0;
interface IFooCallback {};

// hardware/interfaces/bar/1.0/types.hal
package android.hardware.bar@1.0;
typedef string S;

// hardware/interfaces/bar/1.0/IFooCallback.hal
package android.hardware.bar@1.0;
interface IFooCallback {};

// hardware/interfaces/bar/1.0/IBar.hal
package android.hardware.bar@1.0;
import android.hardware.foo@1.0;
interface IBar {
    baz1(S s); // android.hardware.bar@1.0::S
    baz2(IFooCallback s); // android.hardware.foo@1.0::IFooCallback
};
  • S को android.hardware.bar@1.0::S के तौर पर इंटरपोल किया गया है और यह bar/1.0/types.hal में मौजूद है. ऐसा इसलिए है, क्योंकि types.hal अपने-आप इंपोर्ट होता है.
  • नियम 2 का इस्तेमाल करके, IFooCallback को android.hardware.bar@1.0::IFooCallback के तौर पर इंटरपोल किया जाता है. हालांकि, इसे ढूंढा नहीं जा सकता, क्योंकि bar/1.0/IFooCallback.hal को अपने-आप इंपोर्ट नहीं किया जाता (जैसा कि types.hal को किया जाता है). इसलिए, तीसरा नियम इसे android.hardware.foo@1.0::IFooCallback में बदल देता है, जिसे import android.hardware.foo@1.0; के ज़रिए इंपोर्ट किया जाता है).

types.hal

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

types.hal में यूडीटी और import स्टेटमेंट शामिल होते हैं. types.hal को पैकेज के हर इंटरफ़ेस के लिए उपलब्ध कराया जाता है (यह एक इंपोर्ट है), इसलिए ये import स्टेटमेंट, परिभाषा के हिसाब से पैकेज-लेवल के होते हैं. types.hal में मौजूद यूडीटी में, इंपोर्ट किए गए यूडीटी और इंटरफ़ेस भी शामिल किए जा सकते हैं.

उदाहरण के लिए, IFoo.hal के लिए:

package android.hardware.foo@1.0;
// whole package import
import android.hardware.bar@1.0;
// types only import
import android.hardware.baz@1.0::types;
// partial imports
import android.hardware.qux@1.0::IQux.Quux;
// partial imports
import android.hardware.quuz@1.0::Quuz;

ये इंपोर्ट किए जाते हैं:

  • android.hidl.base@1.0::IBase (अनजाने में)
  • android.hardware.foo@1.0::types (अनजाने में)
  • android.hardware.bar@1.0 में मौजूद सभी चीज़ें (सभी इंटरफ़ेस और उसका types.hal शामिल है)
  • android.hardware.baz@1.0::types से types.hal (android.hardware.baz@1.0 में इंटरफ़ेस इंपोर्ट नहीं किए जाते)
  • android.hardware.qux@1.0 से IQux.hal और types.hal
  • android.hardware.quuz@1.0 से Quuz (यह मानते हुए कि Quuz को types.hal में तय किया गया है, पूरी types.hal फ़ाइल को पार्स किया जाता है, लेकिन Quuz के अलावा अन्य टाइप इंपोर्ट नहीं किए जाते).

इंटरफ़ेस-लेवल पर वर्शनिंग

पैकेज में मौजूद हर इंटरफ़ेस, अपनी फ़ाइल में मौजूद होता है. इंटरफ़ेस जिस पैकेज से जुड़ा है उसका एलान, इंटरफ़ेस के सबसे ऊपर package स्टेटमेंट का इस्तेमाल करके किया जाता है. पैकेज के एलान के बाद, इंटरफ़ेस-लेवल के इंपोर्ट (कुछ हिस्से या पूरे पैकेज के) की सूची में शून्य या उससे ज़्यादा इंपोर्ट हो सकते हैं. उदाहरण के लिए:

package android.hardware.nfc@1.0;

HIDL में, इंटरफ़ेस extends कीवर्ड का इस्तेमाल करके, दूसरे इंटरफ़ेस से इनहेरिट कर सकते हैं. किसी इंटरफ़ेस को किसी दूसरे इंटरफ़ेस में शामिल करने के लिए, उसके पास import स्टेटमेंट के ज़रिए उस इंटरफ़ेस का ऐक्सेस होना चाहिए. जिस इंटरफ़ेस को एक्सटेंड किया जा रहा है (बेस इंटरफ़ेस) उसका नाम, ऊपर बताए गए टाइप-नेम की ज़रूरी शर्तों के मुताबिक होना चाहिए. कोई इंटरफ़ेस, सिर्फ़ एक इंटरफ़ेस से इनहेरिट कर सकता है; HIDL, एक से ज़्यादा इनहेरिटेंस के साथ काम नहीं करता.

यहां दिए गए अपग्रेड किए गए वर्शन के उदाहरणों में, इस पैकेज का इस्तेमाल किया गया है:

// types.hal
package android.hardware.example@1.0
struct Foo {
    struct Bar {
        vec<uint32_t> val;
    };
};

// IQuux.hal
package android.hardware.example@1.0
interface IQuux {
    fromFooToBar(Foo f) generates (Foo.Bar b);
}

Uprev के नियम

पैकेज package@major.minor तय करने के लिए, A या B के सभी विकल्पों के लिए सही का निशान होना चाहिए:

पहला नियम "क्या यह शुरुआती माइनर वर्शन है": सभी पिछले माइनर वर्शन, package@major.0, package@major.1, …, package@major.(minor-1) तय नहीं किए जाने चाहिए.
या
नियम B

इनमें से सभी बातें सही हैं:

  1. "पिछला माइनर वर्शन मान्य है": package@major.(minor-1) के लिए वैल्यू तय की जानी चाहिए. साथ ही, यह वैल्यू नियम A (package@major.0 से package@major.(minor-2) तक के किसी भी वर्शन के लिए वैल्यू तय नहीं की गई है) या नियम B (अगर यह @major.(minor-2) से अपग्रेड किया गया वर्शन है) के मुताबिक होनी चाहिए;

    और

  2. "एक ही नाम वाला कम से कम एक इंटरफ़ेस इनहेरिट करें": ऐसा इंटरफ़ेस package@major.minor::IFoo मौजूद है जो package@major.(minor-1)::IFoo को एक्सटेंड करता है (अगर पिछले पैकेज में कोई इंटरफ़ेस है);

    और

  3. "कोई ऐसा इनहेरिट किया गया इंटरफ़ेस नहीं है जिसका नाम अलग हो": ऐसा package@major.minor::IBar नहीं होना चाहिए जो package@major.(minor-1)::IBaz को एक्सटेंड करता हो. यहां IBar और IBaz दो अलग-अलग नाम हैं. अगर एक ही नाम वाला कोई इंटरफ़ेस मौजूद है, तो package@major.minor::IBar को package@major.(minor-k)::IBar को इस तरह एक्सटेंड़ करना चाहिए कि k के छोटे वैल्यू वाला कोई IBar मौजूद न हो.

नियम A की वजह से:

  • पैकेज, किसी भी मामूली वर्शन नंबर से शुरू हो सकता है. उदाहरण के लिए, android.hardware.biometrics.fingerprint, @2.1 से शुरू होता है.
  • "android.hardware.foo@1.0 की जानकारी नहीं दी गई है" शर्त का मतलब है कि डायरेक्ट्री hardware/interfaces/foo/1.0 मौजूद भी नहीं होनी चाहिए.

हालांकि, नियम A का असर उस पैकेज पर नहीं पड़ता जिसका नाम एक ही है, लेकिन मुख्य वर्शन अलग है. उदाहरण के लिए, android.hardware.camera.device में @1.0 और @3.2, दोनों तय किए गए हैं. @3.2 को @1.0 के साथ इंटरैक्ट करने की ज़रूरत नहीं है. इसलिए, @3.2::IExtFoo, @1.0::IFoo को एक्सटेंड कर सकता है.

अगर पैकेज का नाम अलग है, तो package@major.minor::IBar किसी दूसरे नाम वाले इंटरफ़ेस से एक्सटेंड हो सकता है. उदाहरण के लिए, android.hardware.bar@1.0::IBar, android.hardware.baz@2.2::IBaz को एक्सटेंड कर सकता है. अगर कोई इंटरफ़ेस, extend कीवर्ड की मदद से सुपर टाइप को साफ़ तौर पर एलान नहीं करता है, तो वह android.hidl.base@1.0::IBase को एक्सटेंड करता है. हालांकि, IBase को एक्सटेंड नहीं करता.

B.2 और B.3 का एक साथ पालन किया जाना चाहिए. उदाहरण के लिए, अगर B.2 नियम को पूरा करने के लिए, android.hardware.foo@1.1::IFoo ने android.hardware.foo@1.0::IFoo को एक्सटेंड किया है, लेकिन android.hardware.foo@1.1::IExtBar ने android.hardware.foo@1.0::IBar को एक्सटेंड किया है, तो भी यह मान्य अप्रेव नहीं है.

Uprev इंटरफ़ेस

ऊपर बताए गए android.hardware.example@1.0 को @1.1 पर अपग्रेड करने के लिए:

// types.hal
package android.hardware.example@1.1;
import android.hardware.example@1.0;

// IQuux.hal
package android.hardware.example@1.1
interface IQuux extends @1.0::IQuux {
    fromBarToFoo(Foo.Bar b) generates (Foo f);
}

यह types.hal में android.hardware.example के वर्शन 1.0 का पैकेज-लेवल import है. पैकेज के 1.1 वर्शन में कोई नया यूडीटी नहीं जोड़ा गया है. हालांकि, 1.0 वर्शन में यूडीटी के रेफ़रंस अब भी ज़रूरी हैं. इसलिए, types.hal में पैकेज-लेवल पर इंपोर्ट किया गया है. (IQuux.hal में इंटरफ़ेस-लेवल इंपोर्ट की मदद से भी यही असर हासिल किया जा सकता था.)

extends @1.0::IQuux में IQuux के एलान में, हमने IQuux के उस वर्शन के बारे में बताया है जिसे इनहेरिट किया जा रहा है. IQuux का इस्तेमाल, किसी इंटरफ़ेस का एलान करने और किसी इंटरफ़ेस से इनहेरिट करने के लिए किया जाता है. इसलिए, इसे अलग-अलग वर्शन के हिसाब से बताना ज़रूरी है. एलान सिर्फ़ ऐसे नाम होते हैं जो एलान की साइट पर सभी पैकेज और वर्शन एट्रिब्यूट को इनहेरिट करते हैं. इसलिए, अलग-अलग नामों को अलग-अलग करने के लिए, बेस इंटरफ़ेस के नाम का इस्तेमाल किया जाना चाहिए. हम पूरी तरह से क्वालिफ़ाइड यूडीटी का भी इस्तेमाल कर सकते थे, लेकिन यह ज़रूरी नहीं था.

नया इंटरफ़ेस IQuux, fromFooToBar() को @1.0::IQuux से इनहेरिट किए गए तरीके को फिर से एलान नहीं करता है. यह सिर्फ़ उस नए तरीके को सूची में शामिल करता है जिसे fromBarToFoo() जोड़ा जाता है. HIDL में, इनहेरिट किए गए तरीकों को चाइल्ड इंटरफ़ेस में फिर से एलान नहीं किया जा सकता. इसलिए, IQuux इंटरफ़ेस, fromFooToBar() तरीके को साफ़ तौर पर एलान नहीं कर सकता.

Uprev के कन्वेंशन

कभी-कभी इंटरफ़ेस के नामों को, एक्सटेंडेंड इंटरफ़ेस का नाम बदलना पड़ता है. हमारा सुझाव है कि टाइप के एक्सटेंशन, स्ट्रक्चर, और यूनियन का नाम, उस टाइप का ही हो जिसे वे एक्सटेंंड करते हैं. हालांकि, अगर वे टाइप काफ़ी अलग हैं, तो उन्हें नया नाम दिया जा सकता है. उदाहरण:

// in parent hal file
enum Brightness : uint32_t { NONE, WHITE };

// in child hal file extending the existing set with additional similar values
enum Brightness : @1.0::Brightness { AUTOMATIC };

// extending the existing set with values that require a new, more descriptive name:
enum Color : @1.0::Brightness { HW_GREEN, RAINBOW };

अगर किसी तरीके का नया सेमेटिक नाम (उदाहरण के लिए, fooWithLocation) हो सकता है, तो उसे प्राथमिकता दी जाती है. अगर ऐसा नहीं है, तो इसका नाम उस फ़ंक्शन के नाम से मिलता-जुलता होना चाहिए जिसे एक्सटेंड किया जा रहा है. उदाहरण के लिए, अगर कोई बेहतर विकल्प नहीं है, तो @1.1::IFoo में मौजूद foo_1_1 तरीका, @1.0::IFoo में मौजूद foo तरीके की सुविधा को बदल सकता है.

पैकेज-लेवल पर वर्शनिंग

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

हालांकि, एक तरह की रिलेशनशिप को सख्ती से तय किया गया है और इसे लागू करना ज़रूरी है: पैकेज-लेवल पर, पुराने वर्शन के साथ काम करने वाला इनहेरिटेंस. इस स्थिति में, पैरंट पैकेज वह पैकेज होता है जिसे इनहेरिट किया जा रहा है और चाइल्ड पैकेज वह पैकेज होता है जो पैरंट पैकेज को एक्सटेंड करता है. पैकेज-लेवल पर, पुराने सिस्टम के साथ काम करने वाले इनहेरिटेंस के नियम ये हैं:

  1. पैरंट पैकेज के सभी टॉप-लेवल इंटरफ़ेस, चाइल्ड पैकेज के इंटरफ़ेस से इनहेरिट किए जाते हैं.
  2. नए इंटरफ़ेस को नए पैकेज में भी जोड़ा जा सकता है. साथ ही, दूसरे पैकेज में मौजूद अन्य इंटरफ़ेस के साथ संबंधों पर कोई पाबंदी नहीं है.
  3. नए डेटा टाइप को, अपग्रेड किए गए मौजूदा इंटरफ़ेस के नए तरीकों या नए इंटरफ़ेस के ज़रिए इस्तेमाल करने के लिए भी जोड़ा जा सकता है.

इन नियमों को HIDL इंटरफ़ेस-लेवल इनहेरिटेंस और UDT कॉम्पोज़िशन का इस्तेमाल करके लागू किया जा सकता है. हालांकि, यह जानने के लिए कि ये रिलेशनशिप, पुराने वर्शन के साथ काम करने वाले पैकेज एक्सटेंशन का हिस्सा हैं, मेटा-लेवल की जानकारी की ज़रूरत होती है. इस जानकारी का अनुमान इन तरीकों से लगाया जाता है:

अगर कोई पैकेज इस ज़रूरी शर्त को पूरा करता है, तो hidl-gen, पुराने वर्शन के साथ काम करने के नियमों को लागू करता है.