تُنشئ إعلانات بيانات 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 |
تصف الأقسام أدناه أنواع البيانات بمزيد من التفصيل.
تعداد
يصبح التعداد في 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
. إذا ظهرت قيمة في التعداد عدة مرات ، فإن القيمة تظهر في النطاق عدة مرات.
بت فيلد <T>
bitfield<T>
(حيث T
هو تعداد معرف من قبل المستخدم) يصبح النوع الأساسي لذلك التعداد في C ++. في المثال أعلاه ، يصبح حقل البت bitfield<Mode>
uint8_t
.
Vec <T>
يعد قالب الفئة hidl_vec<T>
جزءًا من libhidlbase
ويمكن استخدامه لتمرير متجه من أي نوع HIDL بحجم عشوائي. الحاوية ذات الحجم الثابت المماثلة هي hidl_array
. يمكن أيضًا تهيئة hidl_vec<T>
للإشارة إلى مخزن بيانات خارجي من النوع T
، باستخدام الدالة hidl_vec::setToExternal()
.
بالإضافة إلى إرسال / إدخال البنية بشكل مناسب في رأس C ++ الذي تم إنشاؤه ، فإن استخدام vec<T>
يولد بعض وظائف الراحة للترجمة إلى / من مؤشرات std::vector
و T
عارية. إذا تم استخدام vec<T>
كمعامل ، فسيتم تحميل الوظيفة التي تستخدمها بشكل زائد (سيتم إنشاء نموذجين أوليين) لقبول وتمرير كل من بنية HIDL ونوع std::vector<T>
لهذه المعلمة.
مجموعة مصفوفة
يتم تمثيل المصفوفات الثابتة في hidl بواسطة فئة hidl_array
في libhidlbase
. يمثل hidl_array<T, S1, S2, …, SN>
مجموعة ذات أبعاد ثابتة ذات أبعاد N T[S1][S2]…[SN]
.
سلسلة
يمكن استخدام فئة hidl_string
(جزء من libhidlbase
) لتمرير السلاسل عبر واجهات HIDL ويتم تعريفها في /system/libhidl/base/include/hidl/HidlSupport.h
. يعد موقع التخزين الأول في الفصل مؤشرًا إلى مخزن الأحرف المؤقت الخاص به.
يعرف hidl_string
كيفية التحويل من std::string and char*
(سلسلة C-style) وإليها باستخدام operator=
، و casts الضمنية ، .c_str()
. تحتوي هياكل سلسلة HIDL على منشئي النسخ المناسبين ومشغلي التخصيص من أجل:
- قم بتحميل سلسلة HIDL من سلسلة
std::string
أو سلسلة C. - قم بإنشاء
std::string
جديدة من سلسلة HIDL.
بالإضافة إلى ذلك ، تحتوي سلاسل HIDL على مُنشئ تحويل لذا يمكن استخدام سلاسل C ( char *
) وسلاسل C ++ ( std::string
) في الأساليب التي تأخذ سلسلة HIDL.
هيكل
يمكن أن تحتوي struct
في HIDL على أنواع بيانات ذات حجم ثابت فقط ولا تحتوي على وظائف. يتم تعيين تعريفات هيكل struct
مباشرة إلى هياكل التخطيط القياسي في C ++ ، مما يضمن أن struct
لها تخطيط ذاكرة ثابت. يمكن أن تتضمن البنية أنواع HIDL ، بما في ذلك handle
، string
، و vec<T>
، والتي تشير إلى فصل المخازن المؤقتة ذات الطول المتغير.
يتعامل
تحذير: يجب ألا تكون العناوين من أي نوع (حتى عناوين الأجهزة الفعلية) جزءًا من معالج أصلي. يعد تمرير هذه المعلومات بين العمليات أمرًا خطيرًا ويجعلها عرضة للهجوم. يجب التحقق من صحة أي قيم يتم تمريرها بين العمليات قبل استخدامها للبحث عن الذاكرة المخصصة داخل العملية. خلاف ذلك ، قد تتسبب المقابض التالفة في الوصول السيئ للذاكرة أو تلف الذاكرة.
يتم تمثيل نوع handle
من خلال بنية hidl_handle
في C ++ ، وهو عبارة عن غلاف بسيط حول مؤشر إلى كائن 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
واصفات الملفات المرفقة ما يلي:
- بعد استدعاء الأسلوب
setTo(native_handle_t* handle, bool shouldOwn)
مع تعيين المعلمةshouldOwn
علىtrue
- عندما يتم إنشاء كائن
hidl_handle
عن طريق إنشاء نسخة من كائنhidl_handle
آخر - عندما يتم تعيين نسخة من كائن
hidl_handle
من كائنhidl_handle
آخر
يوفر hidl_handle
ضمنية وصريحة إلى / من كائنات native_handle_t*
. الاستخدام الرئيسي لنوع handle
في HIDL هو تمرير واصفات الملف عبر واجهات HIDL. لذلك يتم تمثيل واصف ملف واحد بواسطة native_handle_t
بدون int
s و fd
واحد. إذا كان العميل والخادم يعيشان في عملية مختلفة ، فسيقوم تنفيذ RPC تلقائيًا برعاية واصف الملف لضمان إمكانية تشغيل كلتا العمليتين على نفس الملف.
على الرغم من أن واصف الملف الذي يتم تلقيه في hidl_handle
بواسطة عملية ما سيكون صالحًا في هذه العملية ، إلا أنه لن يستمر خارج وظيفة الاستلام (سيتم إغلاقه بمجرد عودة الوظيفة). العملية التي تريد الاحتفاظ بوصول مستمر إلى واصف الملف يجب أن dup()
واصفات الملفات المرفقة ، أو تنسخ كائن hidl_handle
بأكمله.
ذاكرة
تعيين نوع memory
HIDL إلى فئة hidl_memory
في libhidlbase
، والتي تمثل ذاكرة مشتركة غير معيّنة. هذا هو الكائن الذي يجب أن يتم تمريره بين العمليات لمشاركة الذاكرة في 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
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<>
.