أنواع البيانات

تُنشئ إعلانات بيانات 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. لاستخدام الذاكرة المشتركة:

  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 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<> .