انواع داده ها

این بخش انواع داده های HIDL را توضیح می دهد. برای جزئیات پیاده‌سازی، HIDL C++ (برای پیاده‌سازی C++) یا HIDL Java (برای پیاده‌سازی جاوا) را ببینید.

شباهت های C++ عبارتند از:

  • structs از نحو C++ استفاده می کنند. unions به طور پیش فرض از سینتکس ++C پشتیبانی می کنند. هر دو باید نامگذاری شوند. ساختارها و اتحادیه های ناشناس پشتیبانی نمی شوند.
  • Typedef ها در HIDL مجاز هستند (همانطور که در C++ هستند).
  • نظرات به سبک C++ مجاز هستند و در فایل هدر تولید شده کپی می شوند.

شباهت های جاوا عبارتند از:

  • برای هر فایل، HIDL یک فضای نام به سبک جاوا تعریف می کند که باید با android.hardware. . فضای نام C++ ایجاد شده ::android::hardware::… است.
  • تمام تعاریف فایل در یک بسته بندی interface به سبک جاوا موجود است.
  • اعلان های آرایه HIDL از سبک جاوا پیروی می کنند، نه از سبک C++. مثال:
    struct Point {
        int32_t x;
        int32_t y;
    };
    Point[3] triangle;   // sized array
    
  • نظرات مشابه فرمت javadoc هستند.

بازنمایی داده ها

یک struct یا union متشکل از Standard-Layout (زیرمجموعه ای از الزامات انواع داده های قدیمی) دارای یک چیدمان حافظه ثابت در کد C++ تولید شده است که با ویژگی های تراز صریح بر روی اعضای struct و union اعمال می شود.

انواع HIDL اولیه، و همچنین انواع enum و bitfield (که همیشه از انواع اولیه مشتق می‌شوند)، به انواع استاندارد C++ مانند std::uint32_t از cstdint نگاشت می‌شوند.

از آنجایی که جاوا از انواع بدون علامت پشتیبانی نمی کند، انواع HIDL بدون علامت به نوع جاوا امضا شده مربوطه نگاشت می شوند. نقشه ساختاری به کلاس های جاوا. نقشه آرایه ها به آرایه های جاوا. اتحادیه ها در حال حاضر در جاوا پشتیبانی نمی شوند. رشته ها در داخل به عنوان UTF8 ذخیره می شوند. از آنجایی که جاوا فقط رشته‌های UTF16 را پشتیبانی می‌کند، مقادیر رشته ارسال شده به یا از یک پیاده‌سازی جاوا ترجمه می‌شوند و ممکن است در ترجمه مجدد یکسان نباشند زیرا مجموعه کاراکترها همیشه هموار نقشه‌برداری نمی‌شوند.

داده‌های دریافتی از طریق IPC در ++C به‌عنوان const علامت‌گذاری شده‌اند و در حافظه فقط خواندنی هستند که فقط برای مدت زمان فراخوانی تابع باقی می‌مانند. داده های دریافت شده از طریق IPC در جاوا قبلاً در اشیاء جاوا کپی شده اند، بنابراین می توان آنها را بدون کپی اضافی حفظ کرد (و ممکن است اصلاح شود).

حاشیه نویسی ها

حاشیه نویسی به سبک جاوا ممکن است به اعلان های نوع اضافه شود. حاشیه نویسی ها توسط مجموعه تست فروشنده (VTS) از کامپایلر HIDL تجزیه می شوند، اما هیچ یک از این حاشیه نویسی های تجزیه شده توسط کامپایلر HIDL درک نمی شوند. در عوض، حاشیه نویسی های تجزیه شده VTS توسط کامپایلر VTS (VTSC) مدیریت می شود.

حاشیه نویسی ها از نحو جاوا استفاده می کنند: @annotation یا @annotation(value) یا @annotation(id=value, id=value…) که در آن مقدار ممکن است یک عبارت ثابت، یک رشته یا لیستی از مقادیر داخل {} باشد، درست مانند جاوا. حاشیه نویسی های متعددی با یک نام را می توان به یک مورد متصل کرد.

اعلامیه های فوروارد

در HIDL، ساختارها ممکن است به جلو اعلام نشوند، که باعث می شود انواع داده های خودارجاعی تعریف شده توسط کاربر غیرممکن شود (به عنوان مثال، شما نمی توانید یک لیست پیوندی یا یک درخت را در HIDL توصیف کنید). اکثر HAL‌های موجود (قبل از Android 8.x) استفاده محدودی از اعلان‌های فوروارد دارند که می‌توان با مرتب کردن مجدد اعلان‌های ساختار داده آن را حذف کرد.

این محدودیت به ساختارهای داده اجازه می دهد تا با یک کپی عمیق ساده، به جای ردیابی مقادیر اشاره گر که ممکن است چندین بار در یک ساختار داده خودارجاعی رخ دهند، به صورت ارزشی کپی شوند. اگر همان داده دو بار ارسال شود، مثلاً با دو پارامتر متد یا vec<T> هایی که به یک داده اشاره می کنند، دو نسخه جداگانه ساخته و تحویل داده می شود.

اعلامیه های تو در تو

HIDL از اعلان‌های تودرتو به هر تعداد سطح دلخواه پشتیبانی می‌کند (به استثنای یک استثنا که در زیر ذکر شده است). مثلا:

interface IFoo {
    uint32_t[3][4][5][6] multidimArray;

    vec<vec<vec<int8_t>>> multidimVector;

    vec<bool[4]> arrayVec;

    struct foo {
        struct bar {
            uint32_t val;
        };
        bar b;
    }
    struct baz {
        foo f;
        foo.bar fb; // HIDL uses dots to access nested type names
    }
    …

استثنا این است که انواع رابط را فقط می توان در vec<T> و تنها یک سطح عمیق (بدون vec<vec<IFoo>> ) جاسازی کرد.

نحو اشاره گر خام

زبان HIDL از * استفاده نمی کند و از انعطاف پذیری کامل نشانگرهای خام C/C++ پشتیبانی نمی کند. برای جزئیات بیشتر در مورد اینکه HIDL چگونه نشانگرها و آرایه ها/بردارها را کپسوله می کند، به الگوی vec<T> مراجعه کنید.

رابط ها

کلمه کلیدی interface دو کاربرد دارد.

  • تعریف یک رابط را در یک فایل hal باز می کند.
  • می توان از آن به عنوان یک نوع خاص در فیلدهای struct/union، پارامترهای متد و بازده استفاده کرد. به عنوان یک رابط عمومی و مترادف با android.hidl.base@1.0::IBase مشاهده می شود.

به عنوان مثال، IServiceManager روش زیر را دارد:

get(string fqName, string name) generates (interface service);

این روش قول می دهد که برخی از رابط ها را با نام جستجو کند. همچنین جایگزین کردن رابط با android.hidl.base@1.0::IBase یکسان است.

رابط ها را فقط می توان به دو روش ارسال کرد: به عنوان پارامترهای سطح بالا، یا به عنوان اعضای یک vec<IMyInterface> . آنها نمی توانند اعضای vecs، ساختارها، آرایه ها یا اتحادیه های تودرتو باشند.

MQDescriptorSync و MQDescriptorUnsync

انواع MQDescriptorSync و MQDescriptorUnsync توصیفگرهای Fast Message Queue (FMQ) همگام یا غیر همگام شده را در یک رابط HIDL ارسال می کنند. برای جزئیات، HIDL C++ را ببینید (FMQها در جاوا پشتیبانی نمی‌شوند).

نوع حافظه

نوع memory برای نمایش حافظه مشترک بدون نقشه در HIDL استفاده می شود. فقط در C++ پشتیبانی می شود. مقداری از این نوع را می توان در انتهای گیرنده برای مقداردهی اولیه یک شی IMemory ، نقشه برداری از حافظه و قابل استفاده کردن آن استفاده کرد. برای جزئیات، HIDL C++ را ببینید.

اخطار: داده‌های ساختاریافته قرار داده شده در حافظه مشترک باید نوعی باشد که قالب آن در طول عمر نسخه رابط که از memory عبور می‌کند هرگز تغییر نخواهد کرد. در غیر این صورت، HAL ها ممکن است دچار مشکلات سازگاری کشنده شوند.

نوع اشاره گر

نوع pointer فقط برای استفاده داخلی HIDL است.

قالب نوع bitfield<T>

bitfield<T> که در آن T یک enum تعریف شده توسط کاربر است، نشان می‌دهد که مقدار یک بیتی-OR از مقادیر enum تعریف شده در T است. در کد تولید شده، bitfield<T> به عنوان نوع زیرین T ظاهر می شود. به عنوان مثال:

enum Flag : uint8_t {
    HAS_FOO = 1 << 0,
    HAS_BAR = 1 << 1,
    HAS_BAZ = 1 << 2
};
typedef bitfield<Flag> Flags;
setFlags(Flags flags) generates (bool success);

کامپایلر نوع Flags را مانند uint8_t مدیریت می کند.

چرا از (u)int8_t / (u)int16_t / (u)int32_t / (u)int64_t استفاده نمی کنید؟ استفاده از bitfield اطلاعات HAL اضافی را برای خواننده فراهم می کند، که اکنون می داند که setFlags یک مقدار بیتی-OR از Flag می گیرد (یعنی می داند که فراخوانی setFlags با 16 نامعتبر است). بدون bitfield ، این اطلاعات فقط از طریق مستندات منتقل می شود. علاوه بر این، VTS در واقع می‌تواند بررسی کند که آیا مقدار پرچم‌ها مقدار بیتی-OR Flag است یا خیر.

دسته بدوی

اخطار: آدرس‌ها از هر نوع (حتی آدرس‌های فیزیکی دستگاه) هرگز نباید بخشی از یک دسته اصلی باشند. انتقال این اطلاعات بین فرآیندها خطرناک است و آنها را مستعد حمله می کند. هر مقداری که بین فرآیندها ارسال می شود باید قبل از استفاده از آنها برای جستجوی حافظه تخصیص یافته در یک فرآیند تأیید شود. در غیر این صورت، دسته های بد ممکن است باعث دسترسی بد به حافظه یا خراب شدن حافظه شوند.

معنای HIDL کپی به ارزش است، که به این معنی است که پارامترها کپی می شوند. هر قطعه بزرگ داده یا داده‌ای که باید بین فرآیندها به اشتراک گذاشته شود (مانند حصار همگام‌سازی)، با عبور از توصیف‌گرهای فایل که به اشیاء ثابت اشاره می‌کنند، مدیریت می‌شوند: ashmem برای حافظه مشترک، فایل‌های واقعی یا هر چیز دیگری که می‌تواند پشت آن پنهان شود. یک توصیفگر فایل درایور بایندر توصیفگر فایل را در فرآیند دیگر کپی می کند.

native_handle_t

Android از native_handle_t پشتیبانی می کند، یک مفهوم دسته کلی که در libcutils تعریف شده است.

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;

هندل بومی مجموعه‌ای از ints و توصیف‌گرهای فایل است که بر اساس مقدار منتقل می‌شود. یک توصیفگر یک فایل را می توان در یک دسته بومی بدون ورودی و یک توصیفگر واحد ذخیره کرد. عبور دسته‌ها با استفاده از دسته‌های بومی محصور شده با نوع اولیه handle ، تضمین می‌کند که دسته‌های بومی مستقیماً در HIDL گنجانده شده‌اند.

از آنجایی که یک native_handle_t دارای اندازه متغیر است، نمی توان آن را مستقیماً در یک ساختار گنجاند. یک فیلد دسته یک اشاره گر به یک native_handle_t اختصاص داده شده به طور جداگانه ایجاد می کند.

در نسخه‌های قبلی اندروید، دسته‌های بومی با استفاده از همان توابع موجود در libcutils ایجاد می‌شدند. در اندروید 8.0 و بالاتر، این توابع اکنون در فضای نام android::hardware::hidl کپی می شوند یا به NDK منتقل می شوند. کدهای خودساز HIDL این توابع را به صورت خودکار و بدون دخالت کدهای نوشته شده توسط کاربر سریال‌سازی می‌کند.

مالکیت توصیفگر مدیریت و فایل

هنگامی که یک متد واسط HIDL را فراخوانی می کنید که یک شی hidl_handle را ارسال می کند (یا برمی گرداند) (اعم از سطح بالا یا بخشی از یک نوع ترکیبی)، مالکیت توصیفگرهای فایل موجود در آن به شرح زیر است:

  • تماس گیرنده که یک شی hidl_handle را به عنوان آرگومان ارسال می کند، مالکیت توصیفگرهای فایل موجود در native_handle_t آن را حفظ می کند. تماس گیرنده باید این توصیفگرهای فایل را پس از اتمام کار با آنها ببندد.
  • فرآیند برگرداندن یک شی hidl_handle (با ارسال آن به یک تابع _cb ) مالکیت توصیفگرهای فایل موجود در native_handle_t که توسط شی پیچیده شده است را حفظ می کند. فرآیند باید این توصیفگرهای فایل را هنگامی که با آنها انجام می شود ببندد.
  • انتقالی که یک hidl_handle دریافت می کند، مالکیت توصیفگرهای فایل را در داخل native_handle_t دارد که توسط شی پیچیده شده است. گیرنده می‌تواند از این توصیف‌گرهای فایل همانطور که هست در طول تماس برگشتی تراکنش استفاده کند، اما باید دسته بومی را شبیه‌سازی کند تا از توصیف‌کننده‌های فایل فراتر از پاسخ به تماس استفاده کند. هنگامی که تراکنش انجام شد، انتقال به طور خودکار توصیفگرهای فایل close() می‌بندد.

HIDL از هندل ها در جاوا پشتیبانی نمی کند (زیرا جاوا اصلاً از دسته ها پشتیبانی نمی کند).

آرایه های اندازه

برای آرایه های با اندازه در ساختارهای HIDL، عناصر آنها می توانند از هر نوعی باشند که یک ساختار می تواند شامل شود:

struct foo {
uint32_t[3] x; // array is contained in foo
};

رشته های

رشته‌ها در C++ و Java متفاوت ظاهر می‌شوند، اما نوع ذخیره‌سازی زیربنایی یک ساختار C++ است. برای جزئیات، به انواع داده های HIDL C++ یا انواع داده های جاوا HIDL مراجعه کنید.

توجه: ارسال یک رشته به یا از جاوا از طریق رابط HIDL (از جمله جاوا به جاوا) باعث تبدیل مجموعه کاراکترهایی می شود که ممکن است دقیقاً رمزگذاری اصلی را حفظ نکنند.

قالب vec<T>

قالب vec<T> یک بافر با اندازه متغیر حاوی نمونه هایی از T را نشان می دهد.

T می تواند یکی از موارد زیر باشد:

  • انواع اولیه (به عنوان مثال uint32_t)
  • رشته های
  • شماره های تعریف شده توسط کاربر
  • ساختارهای تعریف شده توسط کاربر
  • رابط ها یا کلمه کلیدی interface ( vec<IFoo> , vec<interface> فقط به عنوان یک پارامتر سطح بالا پشتیبانی می شود)
  • دستگیره ها
  • bitfield<U>
  • vec<U>، جایی که U در این لیست است به جز رابط (به عنوان مثال vec<vec<IFoo>> پشتیبانی نمی شود)
  • U[] (آرایه با اندازه U)، جایی که U در این لیست به جز رابط است

انواع تعریف شده توسط کاربر

این بخش انواع تعریف شده توسط کاربر را شرح می دهد.

Enum

HIDL از enum های ناشناس پشتیبانی نمی کند. در غیر این صورت، enum ها در HIDL مشابه C++11 هستند:

enum name : type { enumerator , enumerator = constexpr , …  }

یک عدد پایه بر حسب یکی از انواع عدد صحیح در HIDL تعریف می شود. اگر هیچ مقداری برای شمارشگر اول یک enum بر اساس نوع عدد صحیح مشخص نشده باشد، مقدار به طور پیش‌فرض روی 0 است. اگر مقداری برای شمارشگر بعدی مشخص نشده باشد، مقدار پیش‌فرض به مقدار قبلی به اضافه یک می‌شود. مثلا:

// RED == 0
// BLUE == 4 (GREEN + 1)
enum Color : uint32_t { RED, GREEN = 3, BLUE }

یک enum همچنین می تواند از یک enum تعریف شده قبلی ارث ببرد. اگر هیچ مقداری برای اولین شمارشگر یک enum فرزند (در این مورد FullSpectrumColor ) مشخص نشده باشد، مقدار آخرین شمارشگر از enum والد به اضافه یک به طور پیش فرض تعیین می شود. مثلا:

// ULTRAVIOLET == 5 (Color:BLUE + 1)
enum FullSpectrumColor : Color { ULTRAVIOLET }

هشدار: ارث بری Enum نسبت به سایر انواع ارث برعکس عمل می کند. مقدار enum فرزند نمی تواند به عنوان مقدار enum والد استفاده شود. این به این دلیل است که یک عدد فرزند شامل مقادیر بیشتری نسبت به والد است. با این حال، یک مقدار enum والد را می توان به طور ایمن به عنوان یک مقدار enum فرزند استفاده کرد، زیرا مقادیر فرزند enum بنا به تعریف ابرمجموعه ای از مقادیر enum والد هستند. این را در هنگام طراحی رابط‌ها در نظر داشته باشید، زیرا به این معنی است که انواع ارجاع‌دهنده به enum‌های والد نمی‌توانند در تکرارهای بعدی رابط شما به شماره‌های فرزند اشاره کنند.

مقادیر enum ها با دستور دو نقطه (نه نحو نقطه به عنوان انواع تودرتو) نامیده می شوند. نحو Type:VALUE_NAME است. اگر مقدار به همان نوع enum یا نوع فرزند ارجاع داده شود، نیازی به تعیین نوع نیست. مثال:

enum Grayscale : uint32_t { BLACK = 0, WHITE = BLACK + 1 };
enum Color : Grayscale { RED = WHITE + 1 };
enum Unrelated : uint32_t { FOO = Color:RED + 1 };

با شروع در اندروید 10، enum ها دارای ویژگی len هستند که می تواند در عبارات ثابت استفاده شود. MyEnum::len تعداد کل ورودی های آن شمارش است. این با تعداد کل مقادیر متفاوت است، که ممکن است در صورت تکرار مقادیر کمتر باشد.

ساختار

HIDL از ساختارهای ناشناس پشتیبانی نمی کند. در غیر این صورت، ساختارها در HIDL بسیار شبیه به C هستند.

HIDL از ساختارهای داده با طول متغیر که به طور کامل در یک ساختار قرار دارند پشتیبانی نمی کند. این شامل آرایه با طول نامحدود است که گاهی اوقات به عنوان آخرین فیلد یک ساختار در C/C++ استفاده می شود (گاهی اوقات با اندازه [0] دیده می شود). HIDL vec<T> آرایه هایی با اندازه پویا را با داده های ذخیره شده در یک بافر جداگانه نشان می دهد. چنین نمونه هایی با یک نمونه از vec<T> در struct نمایش داده می شوند.

به طور مشابه، string می تواند در یک struct قرار گیرد (بافرهای مرتبط جدا هستند). در C++ تولید شده، نمونه‌هایی از نوع دسته HIDL از طریق یک اشاره‌گر به دسته اصلی واقعی نشان داده می‌شوند، زیرا نمونه‌هایی از نوع داده زیربنایی دارای طول متغیر هستند.

اتحاد. اتصال

HIDL از اتحادیه های ناشناس پشتیبانی نمی کند. در غیر این صورت، اتحادیه ها مشابه C هستند.

اتحادیه ها نمی توانند حاوی انواع اصلاح (اشاره گر، توصیفگر فایل، اشیاء بایندر و غیره) باشند. آنها نیازی به فیلدهای خاص یا انواع مرتبط ندارند و به سادگی از طریق memcpy() یا معادل آن کپی می شوند. یک اتحادیه ممکن است مستقیماً حاوی (یا شامل سایر ساختارهای داده) چیزی نباشد که مستلزم تنظیم افست بایندر باشد (به عنوان مثال، دسته یا مراجع رابط بایندر). مثلا:

union UnionType {
uint32_t a;
//  vec<uint32_t> r;  // Error: can't contain a vec<T>
uint8_t b;1
};
fun8(UnionType info); // Legal

اتحادیه ها همچنین می توانند در داخل ساختارها اعلام شوند. مثلا:

struct MyStruct {
    union MyUnion {
      uint32_t a;
      uint8_t b;
    }; // declares type but not member

    union MyUnion2 {
      uint32_t a;
      uint8_t b;
    } data; // declares type but not member
  }