تنشئ إعلانات بيانات 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
. في حال ظهور قيمة في تعداد
عدة مرات، تظهر القيمة في النطاق عدة مرات.
Bitfield<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
مساحة التخزين الأولى
location في الفئة مؤشر إلى المخزن المؤقت للأحرف الخاصة به.
يعرف hidl_string
كيفية التحويل من وإلى
std::string and char*
(سلسلة من النمط C) باستخدام
operator=
، والبث الضمني، ودالة .c_str()
.
تشتمل بُنى سلسلة HIDL على المنشئات والإنشاءات المناسبة للنسخ
العوامل إلى:
- تحميل سلسلة HIDL من سلسلة
std::string
أو سلسلة C. - إنشاء
std::string
جديد من سلسلة HIDL
بالإضافة إلى ذلك، تحتوي سلاسل HIDL على أدوات إنشاء تحويل بحيث تكون سلاسل C
يمكن استخدام سلاسل (char *
) وسلاسل C++ (std::string
) على
الطرق التي تتخذ سلسلة HIDL.
إنشاء
يمكن أن يحتوي struct
في HIDL فقط على أنواع بيانات ذات حجم ثابت
الأخرى. ترتبط تعريفات هيكل 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
وعنوان واحد
fd
إذا كان العميل والخادم يعيشان في عملية مختلفة، فإن استدعاء إجراء عن بُعد (RPC)
يتولى تنفيذها تلقائيًا واصف الملف لضمان
يمكن أن تعمل كلتا العمليتين على نفس الملف.
رغم استلام واصف الملف في hidl_handle
من خلال
صالحة في هذه العملية، فإنها لا تستمر إلى ما بعد تلقي
(يتم إغلاقها عند عرض الدالة). عملية تريد
الاحتفاظ بإمكانية الوصول الدائم إلى واصف الملف يجب dup()
أو تضمين واصفات الملفات، أو نسخ عنصر hidl_handle
بالكامل.
ذكرى
يرتبط النوع HIDL memory
بالفئة hidl_memory
.
في "libhidlbase
"، وهو ما يمثّل ذكرى مشترَكة لم يتم تخصيصها. هذا هو
هو الكائن الذي يجب تمريره بين العمليات لمشاركة الذاكرة في HIDL. إلى
استخدام الذكريات المشترَكة:
- الحصول على نسخة افتراضية من
IAllocator
(المثيل الوحيد حاليًا "آشم" متاحة) وتستخدمها لتخصيص الذكريات المشتركة. - تُرجع
IAllocator::allocate()
hidl_memory
أي كائن يمكن تمريره من خلال HIDL RPC وتعيينه في عملية باستخدام دالةmapMemory
فيlibhidlmemory
. - تعرض الدالة
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<>
.