HAL इंटरफ़ेस बनाना

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

  • वर्शन वाला (नए कॉन्फ़िगरेशन आइटम जोड़ने के लिए, वेंडर/OEM को साफ़ तौर पर एचएएल को बड़ा करना होगा)
  • अच्छी तरह से दस्तावेज़ में दर्ज किया गया हो
  • SELinux का इस्तेमाल करके ऐक्सेस कंट्रोल करना
  • वेंडर टेस्ट सुइट की मदद से, कॉन्फ़िगरेशन आइटम की जांच करना (रेंज की जांच, आइटम के बीच इंटर-डिपेंडेंसी की जांच वगैरह)
  • C++ और Java, दोनों में अपने-आप जनरेट हुए एपीआई

फ़्रेमवर्क के इस्तेमाल किए गए बिल्ड फ़्लैग की पहचान करना

सबसे पहले उन बिल्ड कॉन्फ़िगरेशन की पहचान करें जिनका इस्तेमाल शर्तों के साथ फ़्रेमवर्क को कंपाइल करने के लिए किया जाता है. इसके बाद, सेट को छोटा करने के लिए, पुराने कॉन्फ़िगरेशन को बंद करें. उदाहरण के लिए, surfaceflinger के लिए, बिल्ड फ़्लैग के इस सेट की पहचान की गई है:

  • TARGET_USES_HWC2
  • TARGET_BOARD_PLATFORM
  • TARGET_DISABLE_TRIPLE_BUFFERING
  • TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS
  • NUM_FRAMEBUFFER_SURFACE_BUFFERS
  • TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK
  • VSYNC_EVENT_PHASE_OFFSET_NS
  • SF_VSYNC_EVENT_PHASE_OFFSET_NS
  • PRESENT_TIME_OFFSET_FROM_VSYNC_NS
  • MAX_VIRTUAL_DISPLAY_DIMENSION

HAL इंटरफ़ेस बनाना

किसी सबसिस्टम के लिए, बिल्ड कॉन्फ़िगरेशन को HAL इंटरफ़ेस के ज़रिए ऐक्सेस किया जाता है. वहीं, कॉन्फ़िगरेशन वैल्यू देने के लिए इंटरफ़ेस, HAL पैकेज android.hardware.configstore (फ़िलहाल, इसका वर्शन 1.0 है) में ग्रुप किए जाते हैं. उदाहरण के लिए, surfaceflinger के लिए HAL इंटरफ़ेस फ़ाइल बनाने के लिए, hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal में:

package android.hardware.configstore@1.0;

interface ISurfaceFlingerConfigs {
    // TO-BE-FILLED-BELOW
};

.hal फ़ाइल बनाने के बाद, Android.bp और Android.mk फ़ाइलों में नई .hal फ़ाइल जोड़ने के लिए, hardware/interfaces/update-makefiles.sh चलाएं.

बिल्ड फ़्लैग के लिए फ़ंक्शन जोड़ना

हर बिल्ड फ़्लैग के लिए, इंटरफ़ेस में एक नया फ़ंक्शन जोड़ें. उदाहरण के लिए, hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal में:

interface ISurfaceFlingerConfigs {
    disableTripleBuffering() generates(OptionalBool ret);
    forceHwcForVirtualDisplays() generates(OptionalBool ret);
    enum NumBuffers: uint8_t {
        USE_DEFAULT = 0,
        TWO = 2,
        THREE = 3,
    };
    numFramebufferSurfaceBuffers() generates(NumBuffers ret);
    runWithoutSyncFramework() generates(OptionalBool ret);
    vsyncEventPhaseOffsetNs generates (OptionalUInt64 ret);
    presentTimeOffsetFromSyncNs generates (OptionalUInt64 ret);
    maxVirtualDisplayDimension() generates(OptionalInt32 ret);
};

फ़ंक्शन जोड़ते समय:

  • नामों को छोटा रखें. Makefile वैरिएबल के नामों को फ़ंक्शन नामों में बदलने से बचें और ध्यान रखें कि TARGET_ और BOARD_ प्रीफ़िक्स अब ज़रूरी नहीं हैं.
  • टिप्पणियां जोड़ें. डेवलपर को कॉन्फ़िगरेशन आइटम के मकसद, फ़्रेमवर्क के व्यवहार में बदलाव करने के तरीके, मान्य वैल्यू, और दूसरी ज़रूरी जानकारी को समझने में मदद करें.

फ़ंक्शन के रिटर्न टाइप Optional[Bool|String|Int32|UInt32|Int64|UInt64] हो सकते हैं. टाइप, एक ही डायरेक्ट्री में types.hal में तय किए जाते हैं. साथ ही, प्राइमिटिव वैल्यू को ऐसे फ़ील्ड में रैप किया जाता है जिससे पता चलता है कि वैल्यू को HAL ने तय किया है या नहीं. अगर नहीं, तो डिफ़ॉल्ट वैल्यू का इस्तेमाल किया जाता है.

struct OptionalString {
    bool specified;
    string value;
};

ज़रूरत पड़ने पर, एन्म्यू को कॉन्फ़िगरेशन आइटम के टाइप के हिसाब से तय करें. साथ ही, उस एन्म्यू का इस्तेमाल रिटर्न टाइप के तौर पर करें. ऊपर दिए गए उदाहरण में, NumBuffers एनम को मान्य वैल्यू की संख्या सीमित करने के लिए तय किया गया है. इस तरह के कस्टम डेटा टाइप तय करते समय, कोई फ़ील्ड या सूची से चुनी जाने वाली वैल्यू (उदाहरण के लिए, USE_DEFAULT) जोड़ें. इससे यह पता चलता है कि वैल्यू, एचएएल के ज़रिए तय की गई है या नहीं.

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

उदाहरण के लिए, hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal में दो बिल्ड फ़्लैग को एक ही स्ट्रक्चर में इकट्ठा करने का विकल्प यह है:

 interface ISurfaceFlingerConfigs {
    // other functions here
    struct SyncConfigs {
        OptionalInt64 vsyncEventPhaseoffsetNs;
        OptionalInt64 presentTimeoffsetFromSyncNs;
    };
    getSyncConfigs() generates (SyncConfigs ret);
    // other functions here
};

HAL फ़ंक्शन के विकल्प

सभी बिल्ड फ़्लैग के लिए, एक ही HAL फ़ंक्शन का इस्तेमाल करने के विकल्प के तौर पर, HAL इंटरफ़ेस में getBoolean(string key) और getInteger(string key) जैसे आसान फ़ंक्शन भी उपलब्ध होते हैं. असल key=value पेयर, अलग-अलग फ़ाइलों में सेव किए जाते हैं. साथ ही, HAL सेवा उन फ़ाइलों को पढ़कर/पार्स करके वैल्यू उपलब्ध कराती है.

इस तरीके को आसानी से समझा जा सकता है. हालांकि, इसमें HIDL के फ़ायदे (वर्शन लागू करना, दस्तावेज़ तैयार करना आसान होना, ऐक्सेस कंट्रोल) शामिल नहीं होते. इसलिए, इसका सुझाव नहीं दिया जाता.

एक और एक से ज़्यादा इंटरफ़ेस

कॉन्फ़िगरेशन आइटम के लिए HAL इंटरफ़ेस के डिज़ाइन में दो विकल्प होते हैं:

  • एक ऐसा इंटरफ़ेस जिसमें सभी कॉन्फ़िगरेशन आइटम शामिल होते हैं
  • एक से ज़्यादा इंटरफ़ेस, जिनमें से हर एक में मिलते-जुलते कॉन्फ़िगरेशन आइटम का एक सेट शामिल होता है

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

इन समस्याओं की वजह से, Android एक ही HAL इंटरफ़ेस के साथ कई इंटरफ़ेस का इस्तेमाल करता है. ऐसा, मिलते-जुलते कॉन्फ़िगरेशन आइटम के ग्रुप के लिए किया जाता है. उदाहरण के लिए, surfaceflinger से जुड़े कॉन्फ़िगरेशन आइटम के लिए ISurfaceflingerConfigs और ब्लूटूथ से जुड़े कॉन्फ़िगरेशन आइटम के लिए IBluetoothConfigs.