HIDL डेटा एलान, C++ स्टैंडर्ड-लेआउट डेटा स्ट्रक्चर जनरेट करते हैं. इन स्ट्रक्चर को कहीं भी रखा जा सकता है, जैसे कि स्टैक पर, फ़ाइल या ग्लोबल स्कोप में या हीप पर. साथ ही, इन्हें एक ही तरह से कंपोज किया जा सकता है. क्लाइंट कोड, कॉन्स्ट रेफ़रंस और प्राइमिटिव टाइप में पास करके, HIDL प्रॉक्सी कोड को कॉल करता है. वहीं, स्टब और प्रॉक्सी कोड, सीरियलाइज़ेशन की जानकारी छिपाते हैं.
ध्यान दें: डेवलपर के लिखे गए कोड को किसी भी समय, डेटा स्ट्रक्चर को साफ़ तौर पर सीरियलाइज़ या डीसीरियलाइज़ करने की ज़रूरत नहीं होती.
नीचे दी गई टेबल में, HIDL प्राइमिटिव को C++ डेटा टाइप पर मैप किया गया है:
HIDL टाइप | C++ टाइप | हेडर/लाइब्रेरी |
---|---|---|
enum |
enum class |
|
uint8_t..uint64_t |
uint8_t..uint64_t |
<stdint.h> |
int8_t..int64_t |
int8_t..int64_t |
<stdint.h> |
float |
float |
|
double |
double |
|
vec<T> |
hidl_vec<T> |
libhidlbase |
T[S1][S2]...[SN] |
T[S1][S2]...[SN] |
|
string |
hidl_string |
libhidlbase |
handle |
hidl_handle |
libhidlbase |
safe_union |
(custom) struct |
|
struct |
struct |
|
union |
union |
|
fmq_sync |
MQDescriptorSync |
libhidlbase |
fmq_unsync |
MQDescriptorUnsync |
libhidlbase |
यहां दिए गए सेक्शन में, डेटा टाइप के बारे में ज़्यादा जानकारी दी गई है.
enum
HIDL में मौजूद कोई एनम, C++ में एनम बन जाता है. उदाहरण के लिए:
enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 }; enum SpecialMode : Mode { NONE = 0, COMPARE = 1 << 2 };
… यह हो जाएगा:
enum class Mode : uint8_t { WRITE = 1, READ = 2 }; enum class SpecialMode : uint8_t { WRITE = 1, READ = 2, NONE = 0, COMPARE = 4 };
Android 10 से, ::android::hardware::hidl_enum_range
का इस्तेमाल करके किसी एनम को दोहराया जा सकता है. इस रेंज में, हर एनोमेरेटर को उसी क्रम में शामिल किया जाता है जिस क्रम में वे HIDL सोर्स कोड में दिखते हैं. यह क्रम, पैरंट एनोमेरेटर से लेकर आखिरी चाइल्ड एनोमेरेटर तक का होता है. उदाहरण के लिए, यह कोड WRITE
, READ
, NONE
, और COMPARE
पर इस क्रम में बार-बार चलता है. ऊपर दिए गए SpecialMode
के हिसाब से:
template <typename T> using hidl_enum_range = ::android::hardware::hidl_enum_range<T> for (SpecialMode mode : hidl_enum_range<SpecialMode>) {...}
hidl_enum_range
, रिवर्स इटरेटर्स को भी लागू करता है और इसका इस्तेमाल constexpr
संदर्भों में किया जा सकता है. अगर कोई वैल्यू, एनोटेशन में कई बार दिखती है, तो वह रेंज में भी कई बार दिखेगी.
bitfield<T>
bitfield<T>
(जहां T
उपयोगकर्ता के तय किए गए एनम है) C++ में उस एनम का बुनियादी टाइप बन जाता है. ऊपर दिए गए उदाहरण में, bitfield<Mode>
uint8_t
बन जाता है.
vec<T>
hidl_vec<T>
क्लास टेंप्लेट, libhidlbase
का हिस्सा है. इसका इस्तेमाल, किसी भी HIDL टाइप के वेक्टर को किसी भी साइज़ में पास करने के लिए किया जा सकता है. तुलना के लिए, तय साइज़ वाला कंटेनर hidl_array
है. hidl_vec::setToExternal()
फ़ंक्शन का इस्तेमाल करके, hidl_vec<T>
को T
टाइप के बाहरी डेटा बफ़र पर ले जाने के लिए भी शुरू किया जा सकता है.
जनरेट किए गए C++ हेडर में स्ट्रक्चर को सही तरीके से उत्सर्जित/शामिल करने के अलावा, vec<T>
का इस्तेमाल करने से, std::vector
और बेर T
पॉइंटर में अनुवाद करने के लिए कुछ सुविधाजनक फ़ंक्शन जनरेट होते हैं. अगर vec<T>
का इस्तेमाल पैरामीटर के तौर पर किया जाता है, तो उस पैरामीटर के लिए, HIDL स्ट्रक्चर और std::vector<T>
टाइप, दोनों को स्वीकार करने और पास करने के लिए, फ़ंक्शन को ओवरलोड किया जाता है (दो प्रोटोटाइप जनरेट किए जाते हैं).
ऐरे
hidl में कॉन्सटेंट ऐरे को libhidlbase
में hidl_array
क्लास से दिखाया जाता है. hidl_array<T, S1, S2, …,
SN>
, N डाइमेंशन वाले तय साइज़ के ऐरे को दिखाता है
T[S1][S2]…[SN]
.
स्ट्रिंग
hidl_string
क्लास (libhidlbase
का हिस्सा) का इस्तेमाल, HIDL इंटरफ़ेस पर स्ट्रिंग भेजने के लिए किया जा सकता है. इसकी जानकारी /system/libhidl/base/include/hidl/HidlSupport.h
में दी गई है. क्लास में पहली स्टोरेज जगह, उसके वर्ण बफ़र का पॉइंटर होती है.
hidl_string
, operator=
, इंप्लिसिट कास्ट, और .c_str()
फ़ंक्शन का इस्तेमाल करके, std::string and char*
(C-स्टाइल स्ट्रिंग) में बदलने और उससे बदलने का तरीका जानता है.
HIDL स्ट्रिंग स्ट्रक्चर में, कॉपी कंस्ट्रक्टर और असाइनमेंट ऑपरेटर होते हैं, ताकि:
std::string
या C स्ट्रिंग से HIDL स्ट्रिंग लोड करें.- HIDL स्ट्रिंग से नया
std::string
बनाएं.
इसके अलावा, HIDL स्ट्रिंग में कन्वर्ज़न कन्स्ट्रक्टर होते हैं, ताकि C स्ट्रिंग (char *
) और C++ स्ट्रिंग (std::string
) का इस्तेमाल उन तरीकों पर किया जा सके जो HIDL स्ट्रिंग लेते हैं.
struct
HIDL में struct
में सिर्फ़ तय साइज़ के डेटा टाइप हो सकते हैं और कोई फ़ंक्शन नहीं हो सकता. HIDL स्ट्रक्चर की परिभाषाएं, सी++ में सीधे स्टैंडर्ड-लेआउट struct
पर मैप होती हैं. इससे यह पक्का होता है कि struct
का मेमोरी लेआउट एक जैसा हो. किसी स्ट्रक्चर में HIDL टाइप शामिल हो सकते हैं. इनमें handle
, string
, और vec<T>
जैसे टाइप शामिल हैं, जो अलग-अलग वैरिएबल-लेंथ बफ़र पर ले जाते हैं.
हैंडल
चेतावनी: किसी भी तरह के पते (यहां तक कि डिवाइस के पते भी) को कभी भी नेटिव हैंडल का हिस्सा नहीं बनाया जाना चाहिए. इस जानकारी को प्रोसेस के बीच भेजना खतरनाक है. इससे, प्रोसेस पर हमले की संभावना बढ़ जाती है. किसी प्रोसेस में, ऐलोकेट की गई मेमोरी को देखने के लिए, प्रोसेस के बीच भेजी गई किसी भी वैल्यू की पुष्टि की जानी चाहिए. ऐसा न करने पर, खराब हैंडल की वजह से मेमोरी का ऐक्सेस खराब हो सकता है या मेमोरी खराब हो सकती है.
handle
टाइप को C++ में hidl_handle
स्ट्रक्चर से दिखाया जाता है. यह const native_handle_t
ऑब्जेक्ट के पॉइंटर के आस-पास एक आसान रैपर होता है. यह Android में लंबे समय से मौजूद है.
typedef struct native_handle { int version; /* sizeof(native_handle_t) */ int numFds; /* number of file descriptors at &data[0] */ int numInts; /* number of ints at &data[numFds] */ int data[0]; /* numFds + numInts ints */ } native_handle_t;
डिफ़ॉल्ट रूप से, hidl_handle
उस native_handle_t
पॉइंटर का मालिकाना हक नहीं लेता जिसे वह रैप करता है. यह सिर्फ़ native_handle_t
के पॉइंटर को सुरक्षित तरीके से सेव करने के लिए मौजूद है, ताकि इसका इस्तेमाल 32- और 64-बिट, दोनों प्रोसेस में किया जा सके.
hidl_handle
के पास, शामिल किए गए फ़ाइल डिस्क्रिप्टर का मालिकाना हक होने की ये स्थितियां हैं:
shouldOwn
पैरामीटर कोtrue
पर सेट करके,setTo(native_handle_t* handle, bool shouldOwn)
तरीके को कॉल करने के बाद- जब
hidl_handle
ऑब्जेक्ट को किसी दूसरेhidl_handle
ऑब्जेक्ट से कॉपी करके बनाया गया हो - जब
hidl_handle
ऑब्जेक्ट को किसी दूसरेhidl_handle
ऑब्जेक्ट से कॉपी-असाइन किया जाता है
hidl_handle
, native_handle_t*
ऑब्जेक्ट में/से इंप्लिसिट और एक्सप्लिसिट, दोनों तरह के कन्वर्ज़न उपलब्ध कराता है. HIDL में handle
टाइप का मुख्य इस्तेमाल, HIDL इंटरफ़ेस पर फ़ाइल डिस्क्रिप्टर पास करने के लिए किया जाता है. इसलिए, किसी एक फ़ाइल के डिस्क्रिप्टर को native_handle_t
से दिखाया जाता है. इसमें कोई int
नहीं होता और एक fd
होता है. अगर क्लाइंट और सर्वर अलग-अलग प्रोसेस में मौजूद हैं, तो आरपीसी लागू करने की सुविधा, फ़ाइल डिस्क्रिप्टर को अपने-आप मैनेज करती है. इससे यह पक्का होता है कि दोनों प्रोसेस एक ही फ़ाइल पर काम कर सकें.
किसी प्रोसेस से hidl_handle
में मिलने वाला फ़ाइल डिस्क्रिप्टर, उस प्रोसेस में मान्य होता है. हालांकि, यह फ़ंक्शन मिलने के बाद नहीं रहता. फ़ंक्शन के बंद होने पर, यह बंद हो जाता है. अगर किसी प्रोसेस को फ़ाइल डिस्क्रिप्टर का ऐक्सेस हमेशा चाहिए, तो उसे dup()
में शामिल फ़ाइल डिस्क्रिप्टर को hidl_handle
ऑब्जेक्ट की पूरी कॉपी बनानी होगी.
यादें
HIDL memory
टाइप, libhidlbase
में hidl_memory
क्लास पर मैप होता है. यह मैप, शेयर की गई ऐसी मेमोरी को दिखाता है जिसे मैप नहीं किया गया है. यह वह ऑब्जेक्ट है जिसे HIDL में मेमोरी शेयर करने के लिए, प्रोसेस के बीच पास किया जाना चाहिए. शेयर की गई मेमोरी का इस्तेमाल करने के लिए:
IAllocator
का कोई इंस्टेंस पाएं (फ़िलहाल सिर्फ़ इंस्टेंस "ashmem" उपलब्ध है) और शेयर की गई मेमोरी को ऐलोकेट करने के लिए उसका इस्तेमाल करें.IAllocator::allocate()
एकhidl_memory
ऑब्जेक्ट दिखाता है, जिसे HIDL RPC के ज़रिए पास किया जा सकता है औरlibhidlmemory
केmapMemory
फ़ंक्शन का इस्तेमाल करके किसी प्रोसेस में मैप किया जा सकता है.mapMemory
,sp<IMemory>
ऑब्जेक्ट का रेफ़रंस दिखाता है. इसका इस्तेमाल, मेमोरी को ऐक्सेस करने के लिए किया जा सकता है. (IMemory
औरIAllocator
की परिभाषाandroid.hidl.memory@1.0
में दी गई है.)
IAllocator
के किसी इंस्टेंस का इस्तेमाल, मेमोरी को ऐलोकेट करने के लिए किया जा सकता है:
#include <android/hidl/allocator/1.0/IAllocator.h> #include <android/hidl/memory/1.0/IMemory.h> #include <hidlmemory/mapping.h> using ::android::hidl::allocator::V1_0::IAllocator; using ::android::hidl::memory::V1_0::IMemory; using ::android::hardware::hidl_memory; .... sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem"); ashmemAllocator->allocate(2048, [&](bool success, const hidl_memory& mem) { if (!success) { /* error */ } // now you can use the hidl_memory object 'mem' or pass it around }));
मेमोरी में असल बदलाव, IMemory
ऑब्जेक्ट के ज़रिए किए जाने चाहिए. ये बदलाव, mem
बनाने वाले पक्ष या HIDL RPC के ज़रिए उसे पाने वाले पक्ष में किए जा सकते हैं.
// Same includes as above sp<IMemory> memory = mapMemory(mem); void* data = memory->getPointer(); memory->update(); // update memory however you wish after calling update and before calling commit data[0] = 42; memory->commit(); // … memory->update(); // the same memory can be updated multiple times // … memory->commit();
इंटरफ़ेस
इंटरफ़ेस को ऑब्जेक्ट के तौर पर पास किया जा सकता है. इंटरफ़ेस शब्द का इस्तेमाल, android.hidl.base@1.0::IBase
टाइप के लिए सिंटैक्टिक शुगर के तौर पर किया जा सकता है. इसके अलावा, मौजूदा इंटरफ़ेस और इंपोर्ट किए गए किसी भी इंटरफ़ेस को टाइप के तौर पर तय किया जाता है.
इंटरफ़ेस रखने वाले वैरिएबल, सटीक पॉइंटर होने चाहिए:
sp<IName>
. इंटरफ़ेस पैरामीटर लेने वाले HIDL फ़ंक्शन, रॉ पॉइंटर को स्ट्रॉन्ग पॉइंटर में बदल देते हैं. इससे, गलत तरीके से काम करने की समस्या होती है (पॉइंटर अचानक मिट सकता है). समस्याओं से बचने के लिए, HIDL इंटरफ़ेस को हमेशा sp<>
के तौर पर सेव करें.