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

การประกาศข้อมูล 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 วิธีใช้หน่วยความจําที่แชร์

  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 หรือฝั่งที่รับ 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<> เสมอเพื่อหลีกเลี่ยงปัญหา