اعلامیه های داده 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 عبور داده شود و با استفاده از تابع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
استفاده کرد. علاوه بر این، رابط فعلی و هر رابط وارد شده به عنوان یک نوع تعریف می شوند.
متغیرهایی که Interfaces را نگه می دارند باید نشانگرهای قوی باشند: sp<IName>
. توابع HIDL که پارامترهای رابط را می گیرند، نشانگرهای خام را به نشانگرهای قوی تبدیل می کنند و باعث رفتار غیر شهودی می شوند (اشاره گر می تواند به طور غیرمنتظره ای پاک شود). برای جلوگیری از مشکلات، همیشه رابط های HIDL را به عنوان sp<>
ذخیره کنید.