Kiểu dữ liệu

Nội dung khai báo dữ liệu HIDL sẽ tạo cấu trúc dữ liệu bố cục chuẩn C++. Các cấu trúc có thể được đặt ở bất kỳ nơi nào tự nhiên (trên ngăn xếp, trong tệp hoặc toàn cục hoặc trên vùng nhớ khối xếp) và có thể được soạn theo cùng một cách. Khách hàng gọi mã proxy HIDL truyền vào tham chiếu cố định và loại nguyên gốc, còn mã stub và mã proxy sẽ ẩn thông tin chi tiết về quá trình chuyển đổi tuần tự.

Lưu ý: Không có mã nào do nhà phát triển viết cần phải chuyển đổi tuần tự hoặc giải tuần tự cấu trúc dữ liệu một cách rõ ràng.

Bảng dưới đây ánh xạ các dữ liệu nguyên gốc HIDL với 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 dưới đây sẽ mô tả chi tiết hơn về các loại dữ liệu.

enum

Một giá trị enum trong HIDL sẽ trở thành một giá trị 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 };

Kể từ Android 10, một enum có thể được lặp lại sử dụng ::android::hardware::hidl_enum_range. Phạm vi này bao gồm mọi giá trị enum theo thứ tự xuất hiện trong mã nguồn HIDL, bắt đầu từ enum mẹ cho đến con cuối cùng. Ví dụ: mã này lặp lại hơn WRITE, READ, NONECOMPARE theo thứ tự đó. Cho trước SpecialMode:

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 biến lặp ngược và có thể được dùng trong constexpr ngữ cảnh. Nếu một giá trị xuất hiện trong bảng liệt kê nhiều lần, giá trị sẽ xuất hiện trong dải ô nhiều lần.

trường bit<T>

bitfield<T> (trong đó T là một enum do người dùng xác định) sẽ 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ể dùng để truyền một vectơ thuộc loại HIDL bất kỳ có kích thước tuỳ ý. Vùng chứa kích thước cố định tương đương là hidl_array. hidl_vec<T> cũng có thể là được khởi tạo để trỏ tới vùng đệm dữ liệu bên ngoài thuộc loại T, 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 hàm được tạo trong tiêu đề C++, việc sử dụng vec<T> sẽ tạo ra sự thuận tiện các hàm dịch sang/từ std::vectorT đơn thuần con trỏ. Nếu vec<T> được dùng làm tham số, hàm này sử dụng nó quá tải (hai nguyên mẫu được tạo) để chấp nhận và truyền cả cấu trúc HIDL và loại std::vector<T> cho điều đó .

mảng

Các mảng không đổi trong hidl được biểu thị bằng lớp hidl_array trong libhidlbase. hidl_array<T, S1, S2, …, SN> đại diện cho mảng kích thước cố định N kích thước T[S1][S2]…[SN].

string

Lớp hidl_string (một phần của libhidlbase) có thể là dùng để truyền chuỗi qua giao diện HIDL và được xác định trong /system/libhidl/base/include/hidl/HidlSupport.h. Bộ nhớ đầu tiên vị trí trong lớp là một con trỏ tới vùng đệm ký tự của lớp đó.

hidl_string biết cách chuyển đổi sang và từ std::string and char* (chuỗi kiểu C) sử dụng operator=, hệ thống truyền ngầm và hàm .c_str(). Cấu trúc chuỗi HIDL có hàm khởi tạo sao chép và chỉ định thích hợp toán tử để:

  • Tải chuỗi HIDL từ một chuỗi std::string hoặc C.
  • Tạo một std::string mới từ chuỗi HIDL.

Ngoài ra, chuỗi HIDL có hàm khởi tạo chuyển đổi nên chuỗi C Chuỗi (char *) và 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ó . Định nghĩa cấu trúc HIDL ánh xạ trực tiếp đến bố cục tiêu chuẩn struct trong C++, đảm bảo rằng struct có một bố cục bộ nhớ nhất quán. Một cấu trúc có thể chứa các loại HIDL, bao gồm handle, stringvec<T>, điều đó trỏ đến các vùng đệm có độ dài biến riêng biệt.

tên người dùng

CẢNH BÁO: Địa chỉ dưới bất kỳ loại nào (kể cả địa chỉ thực tế địa chỉ thiết bị) không được nằm trong tên người dùng gốc. Đạt tiêu chuẩn này thông tin giữa các quy trình đều nguy hiểm và dễ bị tấn công. Bất kỳ giá trị nào được truyền giữa các quá trình đều phải được xác thực trước khi được dùng để xem bộ nhớ được phân bổ trong một tiến trình. Nếu không, tên người dùng không hợp lệ có thể gây ra truy cập bộ nhớ hoặc hỏng bộ nhớ.

Loại handle được biểu thị bằng hidl_handle cấu trúc trong C++, là một trình bao bọc đơn giản xung quanh con trỏ đến Đối tượng const native_handle_t (đối tượng này đã có trong Android cho trong một khoảng thời gian dài).

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 của con trỏ native_handle_t mà nó gói. Nó chỉ tồn tại để đảm bảo an toàn lưu trữ một con trỏ đến native_handle_t để có thể sử dụng con trỏ đó cả tiến trình 32 và 64 bit.

Các trường hợp trong đó hidl_handle sở hữu tệp đi kèm mô tả bao gồm:

  • Theo lệnh gọi đến phương thức setTo(native_handle_t* handle, bool shouldOwn) có tham số shouldOwn được đặt thành true
  • Khi đối tượng hidl_handle được tạo bằng cách tạo bản sao từ một đối tượng hidl_handle khác
  • Khi đối tượng hidl_handle được sao chép từ một đối tượng khác hidl_handle đối tượng

hidl_handle cung cấp cả lượt chuyển đổi ngầm ẩn và tường minh đến/từ đối tượng native_handle_t* . Mục đích sử dụng chính của Loại handle trong HIDL là để truyền chỉ số mô tả tệp qua HIDL giao diện. Do đó, một chỉ số mô tả tệp được biểu thị bằng một native_handle_t không có int và một fd. Nếu máy khách và máy chủ sống trong một tiến trình khác, RPC sẽ tự động xử lý chỉ số mô tả tệp để đảm bảo cả hai quy trình đều có thể hoạt động trên cùng một tệp.

Mặc dù chỉ số mô tả tệp nhận được trong hidl_handle bởi quá trình hợp lệ trong quá trình đó, nó không tồn tại ngoài phạm vi quá trình nhận (hàm sẽ đóng khi hàm trả về). Một quy trình muốn duy trì quyền truy cập liên tục vào chỉ số mô tả tệp phải dup() chỉ số mô tả tệp đính kèm hoặc sao chép toàn bộ đối tượng hidl_handle.

bộ nhớ

Loại HIDL memory liên kết với lớp hidl_memory trong libhidlbase, đại diện cho bộ nhớ dùng chung chưa được liên kết. Đây là đối tượng phải được truyền giữa các quy trình để chia sẻ bộ nhớ trong HIDL. Người nhận sử dụng bộ nhớ dùng chung:

  1. Nhận một thực thể của IAllocator (hiện chỉ có một thực thể) "ashmem" sẵn) và sử dụng nó để phân bổ bộ nhớ dùng chung.
  2. IAllocator::allocate() trả về một hidl_memory đối tượng có thể được truyền qua HIDL RPC và được ánh xạ vào một quy trình sử dụng Hàm mapMemory của libhidlmemory.
  3. mapMemory trả về một tham chiếu đến một Đối tượng sp<IMemory> có thể dùng để truy cập vào bộ nhớ. (IMemoryIAllocator được định nghĩa trong android.hidl.memory@1.0.)

Bạn có thể dùng một thực thể của IAllocator để phân bổ 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
  }));

Các thay đổi thực tế đối với bộ nhớ phải được thực hiện thông qua IMemory ở phía tạo mem hoặc ở cạnh mà nhận được 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

Giao diện có thể được truyền dưới dạng đối tượng. Bạn có thể sử dụng từ giao diện dưới dạng cú pháp dễ hiểu cho kiểu android.hidl.base@1.0::IBase; ngoài ra, giao diện hiện tại và bất kỳ giao diện đã nhập nào đều được xác định làm loại.

Các biến chứa Giao diện phải là con trỏ mạnh: sp<IName>. Hàm HIDL nhận tham số giao diện 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ị xoá đột ngột). Để tránh sự cố, hãy luôn lưu trữ HIDL giao diện dưới dạng sp<>.