اعلامیه های داده HIDL ساختارهای داده با طرح استاندارد C++ را تولید می کنند. این ساختارها را می توان در هر جایی که طبیعی به نظر می رسد (روی پشته، در پرونده یا دامنه جهانی، یا روی پشته) قرار داد و می توان به همان روش ترکیب کرد. کد کلاینت کد پراکسی HIDL را که در مراجع const و انواع اولیه ارسال می شود فراخوانی می کند، در حالی که کد خرد و پروکسی جزئیات سریال سازی را پنهان می کند.
توجه: در هیچ نقطه ای به کدهای نوشته شده توسط توسعه دهنده برای سریال سازی یا غیر سریال سازی ساختارهای داده نیاز نیست.
جدول زیر انواع داده های اولیه 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
یک enum در HIDL به یک enum در 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، یک enum را می توان با استفاده از ::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 یک enum تعریف شده توسط کاربر است) به نوع اصلی آن enum در 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) با استفاده از operator= , casts ضمنی و تابع .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 است (این برای مدت طولانی در اندروید وجود داشته است).
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 را کپی کند.
حافظه
نوع memory HIDL به کلاس hidl_memory در libhidlbase نگاشت می شود، که نشان دهنده حافظه مشترک بدون نقشه است. این شیئی است که برای اشتراک گذاری حافظه در HIDL باید بین فرآیندها ارسال شود. برای استفاده از حافظه مشترک:
- نمونه ای از
IAllocatorرا دریافت کنید (در حال حاضر فقط نمونه "ashmem" موجود است) و از آن برای تخصیص حافظه مشترک استفاده کنید. -
IAllocator::allocate()یک شیhidl_memoryرا برمی گرداند که می تواند از HIDL RPC عبور داده شود و با استفاده از تابعmapMemorylibhidlmemoryدر یک فرآیند نگاشت شود. -
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 استفاده کرد. علاوه بر این، رابط فعلی و هر رابط وارد شده به عنوان یک نوع تعریف می شوند.
متغیرهایی که Interfaces را نگه می دارند باید نشانگرهای قوی باشند: sp<IName> . توابع HIDL که پارامترهای رابط را می گیرند، نشانگرهای خام را به نشانگرهای قوی تبدیل می کنند و باعث رفتار غیر شهودی می شوند (اشاره گر می تواند به طور غیرمنتظره ای پاک شود). برای جلوگیری از مشکلات، همیشه رابط های HIDL را به عنوان sp<> ذخیره کنید.