การประกาศข้อมูล 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
อาร์เรย์ค่าคงที่ใน 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 เป็นต้นไป คุณสามารถวนซ้ำ 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>
เป็นพารามิเตอร์ ฟังก์ชันที่ใช้จะโอเวอร์โหลด (สร้างโปรโตไทป์ 2 รายการ) เพื่อยอมรับและส่งทั้งโครงสร้าง 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=
, การแคสต์โดยนัย และฟังก์ชัน .c_str()
โครงสร้างสตริง HIDL มีตัวสร้างโค้ดคัดลอกและโอเปอเรเตอร์การกําหนดค่าที่เหมาะสมเพื่อดำเนินการต่อไปนี้
- โหลดสตริง HIDL จาก
std::string
หรือสตริง C - สร้าง
std::string
ใหม่จากสตริง HIDL
นอกจากนี้ สตริง HIDL ยังมีตัวสร้างคอนเวอร์ชันเพื่อให้ใช้สตริง C (char *
) และสตริง C++ (std::string
) ในเมธอดที่ใช้สตริง HIDL ได้
struct
struct
ใน HIDL ต้องมีเฉพาะประเภทข้อมูลขนาดคงที่และไม่มีฟังก์ชัน การกําหนดค่าโครงสร้าง 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
และ fd
รายการเดียว หากไคลเอ็นต์และเซิร์ฟเวอร์ทำงานในกระบวนการอื่น การใช้งาน RPC จะจัดการตัวระบุไฟล์โดยอัตโนมัติเพื่อให้ทั้ง 2 กระบวนการทำงานกับไฟล์เดียวกันได้
แม้ว่าตัวระบุไฟล์ที่ได้รับใน hidl_handle
จากกระบวนการจะถูกต้องในกระบวนการนั้น แต่ตัวระบุไฟล์จะไม่คงอยู่หลังจากฟังก์ชันที่รับ (จะปิดเมื่อฟังก์ชันแสดงผล) กระบวนการที่ต้องการเก็บรักษาสิทธิ์เข้าถึงตัวระบุไฟล์ไว้อย่างถาวรต้องdup()
ตัวระบุไฟล์ที่ปิดอยู่ หรือคัดลอกออบเจ็กต์ hidl_handle
ทั้งหมด
ความทรงจำ
ประเภท HIDL memory
จับคู่กับคลาส 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
หรือฝั่งที่รับ 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();
อินเทอร์เฟซ
อินเทอร์เฟซสามารถส่งเป็นออบเจ็กต์ได้ คุณสามารถใช้คําว่า interface เป็นรูปแบบคำสั่งที่ง่ายขึ้นสําหรับประเภท android.hidl.base@1.0::IBase
นอกจากนี้ ระบบจะกําหนดอินเทอร์เฟซปัจจุบันและอินเทอร์เฟซที่นําเข้าเป็นประเภท
ตัวแปรที่เก็บอินเทอร์เฟซควรเป็นพอยน์เตอร์แบบ Strong ดังนี้
sp<IName>
ฟังก์ชัน HIDL ที่ใช้พารามิเตอร์อินเทอร์เฟซจะแปลงพอยน์เตอร์ดิบเป็นพอยน์เตอร์แบบ Strong ซึ่งทําให้ทํางานได้ไม่ตรงกับที่คาดไว้ (ระบบอาจล้างพอยน์เตอร์โดยไม่คาดคิด) โปรดจัดเก็บอินเทอร์เฟซ HIDL เป็น sp<>
เสมอเพื่อหลีกเลี่ยงปัญหา