การประกาศข้อมูล 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 };
เริ่มตั้งแต่ 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_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++ ซึ่งเป็น Wrapper ง่ายๆ ที่ชี้ไปยัง
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
ให้ Conversion ทั้งโดยนัยและโดยชัดแจ้ง
ไปยัง/จากnative_handle_t*
ออบเจ็กต์ การใช้งานหลักสำหรับ
handle
ประเภทใน HIDL คือการส่งข้อบ่งชี้ไฟล์ผ่าน HIDL
อินเทอร์เฟซ ดังนั้น ตัวอธิบายไฟล์เดี่ยวจะแสดงด้วยองค์ประกอบ
native_handle_t
ที่ไม่มี int
และมีรายการเดียว
fd
หากลูกค้าและเซิร์ฟเวอร์อยู่ในกระบวนการอื่น RPC
จะดูแลข้อบ่งชี้ไฟล์โดยอัตโนมัติเพื่อให้
ทั้ง 2 กระบวนการจะทำงานในไฟล์เดียวกันได้
แม้ว่าตัวระบุไฟล์ที่ได้รับใน 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<>