ประเภทข้อมูล

การประกาศข้อมูล 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 หากต้องการใช้หน่วยความจำที่ใช้ร่วมกัน:

  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 ; นอกจากนี้ อินเทอร์เฟซปัจจุบันและอินเทอร์เฟซที่นำเข้าใดๆ จะถูกกำหนดเป็นประเภท

ตัวแปรที่เก็บอินเทอร์เฟซควรเป็นตัวชี้ที่ชัดเจน: sp<IName> ฟังก์ชัน HIDL ที่ใช้พารามิเตอร์อินเทอร์เฟซจะแปลงพอยน์เตอร์ดิบไปเป็นพอยน์เตอร์ที่รัดกุม ทำให้เกิดพฤติกรรมที่ไม่เป็นธรรมชาติ (ตัวชี้สามารถล้างได้โดยไม่คาดคิด) เพื่อหลีกเลี่ยงปัญหา ให้เก็บอินเทอร์เฟซ HIDL ไว้เป็น sp<> เสมอ