Kiểu dữ liệu

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 , NONECOMPARE 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 , stringvec<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ành true
  • 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ượng hidl_handle khác
  • Khi đối tượng hidl_handle được gán bản sao từ một đối tượng hidl_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:

  1. 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.
  2. IAllocator::allocate() trả về một đối tượng hidl_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àm mapMemory của libhidlmemory .
  3. mapMemory trả về một tham chiếu đến đối tượng sp<IMemory> có thể được sử dụng để truy cập bộ nhớ. ( IMemoryIAllocator được xác định trong android.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<> .