Khai báo dữ liệu HIDL tạo ra cấu trúc dữ liệu bố cục tiêu chuẩn C++. Các cấu trúc này có thể được đặt ở bất cứ đâu mà bạn cảm thấy tự nhiên (trên ngăn xếp, ở phạm vi tệp hoặc toàn cục hoặc trên vùng heap) và có thể được cấu thành theo cùng một kiểu. Mã máy khách gọi mã proxy HIDL truyền vào các tham chiếu const và các kiểu nguyên thủy, trong khi mã sơ khai và mã proxy ẩn các chi tiết về tuần tự hóa.
Lưu ý: Không cần phải có mã do nhà phát triển viết để tuần tự hóa hoặc giải tuần tự hóa cấu trúc dữ liệu một cách rõ ràng.
Bảng bên dưới ánh xạ các nguyên hàm HIDL sang các kiểu dữ liệu C++:
Loại HIDL | Loại C++ | Tiêu đề/Thư viện |
---|---|---|
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 |
Các phần bên dưới mô tả các loại dữ liệu chi tiết hơn.
liệt kê
Một enum trong HIDL trở thành một enum trong C++. Ví dụ:
enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 }; enum SpecialMode : Mode { NONE = 0, COMPARE = 1 << 2 };
… trở thành:
enum class Mode : uint8_t { WRITE = 1, READ = 2 }; enum class SpecialMode : uint8_t { WRITE = 1, READ = 2, NONE = 0, COMPARE = 4 };
Bắt đầu từ Android 10, một enum có thể được lặp lại bằng cách sử dụng ::android::hardware::hidl_enum_range
. Phạm vi này bao gồm mọi điều tra viên theo thứ tự xuất hiện trong mã nguồn HIDL, bắt đầu từ enum cha cho đến enum con cuối cùng. Ví dụ: mã này lặp lại WRITE
, READ
, NONE
và COMPARE
theo thứ tự đó. Đưa ra SpecialMode
ở trên:
template <typename T> using hidl_enum_range = ::android::hardware::hidl_enum_range<T> for (SpecialMode mode : hidl_enum_range<SpecialMode>) {...}
hidl_enum_range
cũng triển khai các trình vòng lặp ngược và có thể được sử dụng trong ngữ cảnh constexpr
. Nếu một giá trị xuất hiện trong bảng liệt kê nhiều lần thì giá trị đó sẽ xuất hiện trong phạm vi nhiều lần.
trường bit<T>
bitfield<T>
(trong đó T
là enum do người dùng xác định) trở thành loại cơ bản của enum đó trong C++. Trong ví dụ trên, bitfield<Mode>
trở thành uint8_t
.
vec<T>
Mẫu lớp hidl_vec<T>
là một phần của libhidlbase
và có thể được sử dụng để truyền vectơ thuộc bất kỳ loại HIDL nào với kích thước tùy ý. Vùng chứa có kích thước cố định có thể so sánh được là hidl_array
. Một hidl_vec<T>
cũng có thể được khởi tạo để trỏ tới bộ đệm dữ liệu ngoài thuộc loại T
, bằng cách sử dụng hàm hidl_vec::setToExternal()
.
Ngoài việc phát/chèn cấu trúc một cách thích hợp trong tiêu đề C++ được tạo, việc sử dụng vec<T>
còn tạo ra một số hàm tiện lợi để dịch sang/từ std::vector
và các con trỏ T
trần. Nếu vec<T>
được sử dụng làm tham số, hàm sử dụng nó sẽ bị quá tải (hai nguyên mẫu sẽ được tạo) để chấp nhận và chuyển cả cấu trúc HIDL và loại std::vector<T>
cho tham số đó.
mảng
Các mảng không đổi trong hidl được biểu thị bằng lớp hidl_array
trong libhidlbase
. Một hidl_array<T, S1, S2, …, SN>
đại diện cho một mảng có kích thước cố định N chiều T[S1][S2]…[SN]
.
sợi dây
Lớp hidl_string
(một phần của libhidlbase
) có thể được sử dụng để truyền chuỗi qua giao diện HIDL và được xác định trong /system/libhidl/base/include/hidl/HidlSupport.h
. Vị trí lưu trữ đầu tiên trong lớp là một con trỏ tới bộ đệm ký tự của nó.
hidl_string
biết cách chuyển đổi sang và từ std::string and char*
(chuỗi kiểu C) bằng cách sử dụng operator=
, các phôi ẩn và .c_str()
. Cấu trúc chuỗi HIDL có các hàm tạo sao chép và toán tử gán thích hợp để:
- Tải chuỗi HIDL từ
std::string
hoặc chuỗi C. - Tạo một
std::string
mới từ chuỗi HIDL.
Ngoài ra, chuỗi HIDL có các hàm tạo chuyển đổi nên chuỗi C ( char *
) và chuỗi C++ ( std::string
) có thể được sử dụng trên các phương thức lấy chuỗi HIDL.
cấu trúc
struct
trong HIDL chỉ có thể chứa các kiểu dữ liệu có kích thước cố định và không có hàm. Các định nghĩa cấu trúc HIDL ánh xạ trực tiếp tới struct
bố cục tiêu chuẩn trong C++, đảm bảo rằng struct
đó có bố cục bộ nhớ nhất quán. Một cấu trúc có thể bao gồm các loại HIDL, bao gồm handle
, string
và vec<T>
, trỏ đến các bộ đệm có độ dài thay đổi riêng biệt.
xử lý
CẢNH BÁO: Địa chỉ thuộc bất kỳ loại nào (kể cả địa chỉ thiết bị vật lý) không bao giờ được là một phần của địa chỉ gốc. Việc truyền thông tin này giữa các tiến trình là nguy hiểm và khiến chúng dễ bị tấn công. Mọi giá trị được truyền giữa các tiến trình đều phải được xác thực trước khi được sử dụng để tra cứu bộ nhớ được phân bổ trong một tiến trình. Nếu không, việc xử lý không tốt có thể gây ra tình trạng truy cập bộ nhớ kém hoặc hỏng bộ nhớ.
Loại handle
được biểu thị bằng cấu trúc hidl_handle
trong C++, đây là một trình bao bọc đơn giản bao quanh một con trỏ tới đối tượng const native_handle_t
(điều này đã có trong Android từ lâu).
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;
Theo mặc định, hidl_handle
không có quyền sở hữu con trỏ native_handle_t
mà nó bao bọc. Nó chỉ tồn tại để lưu trữ một cách an toàn một con trỏ tới native_handle_t
sao cho nó có thể được sử dụng trong cả quy trình 32 và 64 bit.
Các kịch bản trong đó hidl_handle
sở hữu các bộ mô tả tệp kèm theo bao gồm:
- Thực hiện lệnh gọi đến phương
setTo(native_handle_t* handle, bool shouldOwn)
với tham sốshouldOwn
được đặt thànhtrue
- Khi đối tượng
hidl_handle
được tạo bằng cách sao chép cấu trúc từ một đối tượnghidl_handle
khác - Khi đối tượng
hidl_handle
được gán bản sao từ một đối tượnghidl_handle
khác
hidl_handle
cung cấp cả chuyển đổi ngầm định và rõ ràng đến/từ các đối tượng native_handle_t*
. Công dụng chính của loại handle
trong HIDL là chuyển các bộ mô tả tệp qua giao diện HIDL. Do đó, một bộ mô tả tệp duy nhất được biểu thị bằng một native_handle_t
không có int
s và một fd
duy nhất. Nếu máy khách và máy chủ hoạt động trong một quy trình khác, việc triển khai RPC sẽ tự động xử lý bộ mô tả tệp để đảm bảo cả hai quy trình có thể hoạt động trên cùng một tệp.
Mặc dù bộ mô tả tệp mà một quy trình nhận được trong hidl_handle
sẽ hợp lệ trong quy trình đó, nhưng nó sẽ không tồn tại ngoài chức năng nhận (nó sẽ bị đóng sau khi hàm trả về). Một quá trình muốn duy trì quyền truy cập liên tục vào bộ mô tả tệp phải sao dup()
các bộ mô tả tệp kèm theo hoặc sao chép toàn bộ đối tượng hidl_handle
.
ký ức
Loại bộ memory
HIDL ánh xạ tới lớp hidl_memory
trong libhidlbase
, đại diện cho bộ nhớ chia sẻ chưa được ánh xạ. Đây là đối tượng phải được chuyển giữa các tiến trình để chia sẻ bộ nhớ trong HIDL. Để sử dụng bộ nhớ dùng chung:
- Lấy một phiên bản của
IAllocator
(hiện chỉ có phiên bản "ashmem") và sử dụng nó để phân bổ bộ nhớ dùng chung. -
IAllocator::allocate()
trả về một đối tượnghidl_memory
có thể được truyền qua HIDL RPC và được ánh xạ vào một quy trình bằng cách sử dụng hàmmapMemory
củalibhidlmemory
. -
mapMemory
trả về một tham chiếu đến đối tượngsp<IMemory>
có thể được sử dụng để truy cập bộ nhớ. (IMemory
vàIAllocator
được xác định trongandroid.hidl.memory@1.0
.)
Một phiên bản của IAllocator
có thể được sử dụng để cấp phát bộ nhớ:
#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 }));
Những thay đổi thực sự đối với bộ nhớ phải được thực hiện thông qua đối tượng IMemory
, ở bên đã tạo mem
hoặc ở bên nhận nó qua 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();
giao diện
Các giao diện có thể được truyền dưới dạng đối tượng. Giao diện từ có thể được sử dụng làm đường cú pháp cho loại android.hidl.base@1.0::IBase
; Ngoài ra, giao diện hiện tại và mọi giao diện đã nhập sẽ được xác định là một loại.
Các biến chứa Giao diện phải là con trỏ mạnh: sp<IName>
. Các hàm HIDL lấy tham số giao diện sẽ chuyển đổi con trỏ thô thành con trỏ mạnh, gây ra hành vi không trực quan (con trỏ có thể bị xóa bất ngờ). Để tránh sự cố, hãy luôn lưu trữ giao diện HIDL dưới dạng sp<>
.