การประกาศข้อมูล HIDL สร้างโครงสร้างข้อมูลเค้าโครงมาตรฐาน C++ โครงสร้างเหล่านี้สามารถวางได้ทุกที่ที่ให้ความรู้สึกเป็นธรรมชาติ (บนสแต็ก ที่ไฟล์หรือขอบเขตส่วนกลาง หรือบนฮีป) และสามารถประกอบได้ในลักษณะเดียวกัน รหัสไคลเอ็นต์เรียกรหัสพร็อกซี HIDL ที่ส่งผ่านการอ้างอิง const และประเภทดั้งเดิม ในขณะที่รหัส stub และพร็อกซีซ่อนรายละเอียดของการทำให้เป็นอนุกรม
หมายเหตุ: ไม่จำเป็นต้องเขียนโค้ดโดยนักพัฒนาเพื่อทำให้โครงสร้างข้อมูลเป็นอนุกรมหรือดีซีเรียลไลซ์อย่างชัดเจน
ตารางด้านล่างแมป 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 จะกลายเป็น 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 };
เริ่มต้นใน 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
ถ้าค่าปรากฏในการแจกแจงหลายครั้ง ค่านั้นจะปรากฏในช่วงหลายครั้ง
บิตฟิลด์<T>
bitfield<T>
(โดยที่ T
คือ enum ที่ผู้ใช้กำหนด) จะกลายเป็นประเภทพื้นฐานของ enum นั้นใน C++ ในตัวอย่างข้างต้น bitfield<Mode>
จะกลายเป็น uint8_t
เวค<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=
, การแคสต์โดยนัย และฟังก์ชัน .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
(มีอยู่ใน 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
s และ 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
; นอกจากนี้ อินเทอร์เฟซปัจจุบันและอินเทอร์เฟซที่นำเข้าใดๆ จะถูกกำหนดเป็นประเภท
ตัวแปรที่เก็บอินเทอร์เฟซควรเป็นตัวชี้ที่ชัดเจน: sp<IName>
ฟังก์ชัน HIDL ที่ใช้พารามิเตอร์อินเทอร์เฟซจะแปลงพอยน์เตอร์ดิบไปเป็นพอยน์เตอร์ที่รัดกุม ทำให้เกิดพฤติกรรมที่ไม่เป็นธรรมชาติ (ตัวชี้สามารถล้างได้โดยไม่คาดคิด) เพื่อหลีกเลี่ยงปัญหา ให้เก็บอินเทอร์เฟซ HIDL ไว้เป็น sp<>
เสมอ