डेटा प्रकार

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 };

एंड्रॉइड 10 में शुरू करके, एक एनम को ::android::hardware::hidl_enum_range का उपयोग करके पुनरावृत्त किया जा सकता है। इस श्रेणी में प्रत्येक गणनाकार को उसी क्रम में शामिल किया गया है, जिस क्रम में वह एचआईडीएल स्रोत कोड में दिखाई देता है, मूल गणना से शुरू होकर अंतिम बच्चे तक। उदाहरण के लिए, यह कोड उसी क्रम में 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 संदर्भों में किया जा सकता है। यदि कोई मान किसी गणना में कई बार दिखाई देता है, तो मान श्रेणी में कई बार दिखाई देता है।

बिटफ़ील्ड<T>

bitfield<T> (जहाँ T एक उपयोगकर्ता द्वारा परिभाषित एनम है) C++ में उस एनम का अंतर्निहित प्रकार बन जाता है। उपरोक्त उदाहरण में, bitfield<Mode> uint8_t बन जाता है।

vec<T>

hidl_vec<T> क्लास टेम्पलेट libhidlbase का हिस्सा है और इसका उपयोग किसी भी HIDL प्रकार के वेक्टर को मनमाने आकार के साथ पास करने के लिए किया जा सकता है। तुलनीय निश्चित आकार का कंटेनर hidl_array है। hidl_vec::setToExternal() फ़ंक्शन का उपयोग करके, T प्रकार के बाहरी डेटा बफर को इंगित करने के लिए hidl_vec<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 स्ट्रिंग स्ट्रक्चर में उपयुक्त कॉपी कंस्ट्रक्टर और असाइनमेंट ऑपरेटर हैं:

  • HIDL स्ट्रिंग को std::string या C स्ट्रिंग से लोड करें।
  • HIDL स्ट्रिंग से एक नई std::string बनाएं।

इसके अलावा, HIDL स्ट्रिंग्स में रूपांतरण कंस्ट्रक्टर होते हैं इसलिए C स्ट्रिंग्स ( char * ) और C++ स्ट्रिंग्स ( std::string ) का उपयोग HIDL स्ट्रिंग लेने वाले तरीकों पर किया जा सकता है।

struct

HIDL में एक struct में केवल निश्चित आकार के डेटा प्रकार हो सकते हैं और कोई फ़ंक्शन नहीं हो सकता है। HIDL संरचना परिभाषाएँ सीधे C++ में मानक-लेआउट struct s पर मैप करती हैं, यह सुनिश्चित करती हैं कि struct s में एक सुसंगत मेमोरी लेआउट हो। एक संरचना में HIDL प्रकार शामिल हो सकते हैं, जिनमें handle , string और vec<T> शामिल हैं, जो अलग-अलग चर-लंबाई बफ़र्स को इंगित करते हैं।

सँभालना

चेतावनी: किसी भी प्रकार के पते (भौतिक डिवाइस पते भी) कभी भी मूल हैंडल का हिस्सा नहीं होने चाहिए। प्रक्रियाओं के बीच इस जानकारी को प्रसारित करना खतरनाक है और उन पर हमला होने की आशंका है। किसी प्रक्रिया के भीतर आवंटित मेमोरी को देखने के लिए उपयोग किए जाने से पहले प्रक्रियाओं के बीच पारित किसी भी मान को मान्य किया जाना चाहिए। अन्यथा, ख़राब हैंडल ख़राब मेमोरी एक्सेस या मेमोरी भ्रष्टाचार का कारण बन सकते हैं।

handle प्रकार को C++ में hidl_handle संरचना द्वारा दर्शाया गया है, जो एक const native_handle_t ऑब्जेक्ट के पॉइंटर के चारों ओर एक साधारण आवरण है (यह लंबे समय से एंड्रॉइड में मौजूद है)।

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 s और एक एकल fd नहीं होता है। यदि क्लाइंट और सर्वर एक अलग प्रक्रिया में रहते हैं, तो आरपीसी कार्यान्वयन स्वचालित रूप से फ़ाइल डिस्क्रिप्टर का ध्यान रखेगा ताकि यह सुनिश्चित हो सके कि दोनों प्रक्रियाएं एक ही फ़ाइल पर काम कर सकती हैं।

हालाँकि किसी प्रक्रिया द्वारा hidl_handle में प्राप्त फ़ाइल डिस्क्रिप्टर उस प्रक्रिया में मान्य होगा, यह प्राप्त करने वाले फ़ंक्शन से आगे नहीं रहेगा (फ़ंक्शन वापस आने पर यह बंद हो जाएगा)। एक प्रक्रिया जो फ़ाइल डिस्क्रिप्टर तक लगातार पहुंच बनाए रखना चाहती है, उसे संलग्न फ़ाइल डिस्क्रिप्टर को dup() होगा, या संपूर्ण hidl_handle ऑब्जेक्ट को कॉपी करना होगा।

याद

HIDL memory प्रकार libhidlbase में hidl_memory क्लास में मैप होता है, जो अनमैप्ड साझा मेमोरी का प्रतिनिधित्व करता है। यह वह ऑब्जेक्ट है जिसे HIDL में मेमोरी साझा करने के लिए प्रक्रियाओं के बीच पारित किया जाना चाहिए। साझा मेमोरी का उपयोग करने के लिए:

  1. IAllocator का एक उदाहरण प्राप्त करें (वर्तमान में केवल "ashmem" उदाहरण उपलब्ध है) और साझा मेमोरी आवंटित करने के लिए इसका उपयोग करें।
  2. IAllocator::allocate() एक hidl_memory ऑब्जेक्ट लौटाता है जिसे HIDL RPC के माध्यम से पारित किया जा सकता है और libhidlmemory के mapMemory फ़ंक्शन का उपयोग करके एक प्रक्रिया में मैप किया जा सकता है।
  3. 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 इंटरफ़ेस को हमेशा sp<> के रूप में संग्रहीत करें।