انواع داده ها

اعلامیه های داده 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 باید بین فرآیندها ارسال شود. برای استفاده از حافظه مشترک:

  1. نمونه ای از IAllocator دریافت کنید (در حال حاضر فقط نمونه "ashmem" موجود است) و از آن برای تخصیص حافظه مشترک استفاده کنید.
  2. IAllocator::allocate() یک شی hidl_memory را برمی گرداند که می تواند از HIDL RPC عبور داده شود و با استفاده از تابع mapMemory libhidlmemory در یک فرآیند نگاشت شود.
  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 ایجاد کرده است یا در سمتی که آن را از طریق 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<> ذخیره کنید.