Phần này mô tả các loại dữ liệu HIDL. Để biết thông tin chi tiết về cách triển khai, hãy xem phần HIDL C++ (đối với các phương thức triển khai C++) hoặc HIDL Java (đối với các phương thức triển khai Java).
Có các điểm tương đồng với C++ như sau:
structs
sử dụng cú pháp C++;unions
hỗ trợ cú pháp C++ theo mặc định. Cả hai đều phải được đặt tên; các cấu trúc và liên kết ẩn danh không được hỗ trợ.- Bạn được phép sử dụng Typedef trong HIDL (như trong C++).
- Các nhận xét kiểu C++ được cho phép và được sao chép vào tệp tiêu đề được tạo.
Có những điểm tương đồng với Java như sau:
- Đối với mỗi tệp, HIDL xác định một không gian tên kiểu Java phải bắt đầu bằng
android.hardware.
. Không gian tên C++ được tạo là::android::hardware::…
. - Tất cả các định nghĩa của tệp đều nằm trong trình bao bọc
interface
kiểu Java. - Nội dung khai báo mảng HIDL tuân theo kiểu Java, chứ không phải kiểu C++. Ví dụ:
struct Point { int32_t x; int32_t y; }; Point[3] triangle; // sized array
- Các nhận xét tương tự như định dạng javadoc.
Biểu diễn dữ liệu
struct
hoặc union
bao gồm Bố cục tiêu chuẩn (một tập hợp con của yêu cầu về các loại dữ liệu cũ) có bố cục bộ nhớ nhất quán trong mã C++ được tạo, được thực thi bằng các thuộc tính căn chỉnh rõ ràng trên các thành phần struct
và union
.
Các loại HIDL nguyên thuỷ, cũng như các loại enum
và bitfield
(luôn bắt nguồn từ các loại nguyên thuỷ), ánh xạ đến các loại C++ chuẩn như std::uint32_t
từ cstdint.
Vì Java không hỗ trợ các loại không dấu, nên các loại HIDL không dấu được ánh xạ đến loại Java đã ký tương ứng. Cấu trúc ánh xạ đến các lớp Java; mảng ánh xạ đến các mảng Java; kết hợp hiện không được hỗ trợ trong Java. Chuỗi được lưu trữ nội bộ dưới dạng UTF8. Vì Java chỉ hỗ trợ chuỗi UTF16, nên các giá trị chuỗi được gửi đến hoặc từ một quá trình triển khai Java sẽ được dịch và có thể không giống nhau khi dịch lại vì các bộ ký tự không phải lúc nào cũng ánh xạ liền mạch.
Dữ liệu nhận được qua IPC trong C++ được đánh dấu là const
và nằm trong bộ nhớ chỉ có thể đọc, chỉ tồn tại trong thời gian gọi hàm. Dữ liệu nhận được qua IPC trong Java đã được sao chép vào các đối tượng Java, vì vậy, dữ liệu này có thể được giữ lại mà không cần sao chép thêm (và có thể được sửa đổi).
Chú thích
Bạn có thể thêm chú thích kiểu Java vào phần khai báo loại. Các chú thích được phân tích cú pháp bằng phần phụ trợ Bộ kiểm thử của nhà cung cấp (VTS) của trình biên dịch HIDL, nhưng trình biên dịch HIDL không thực sự hiểu được bất kỳ chú thích nào được phân tích cú pháp như vậy. Thay vào đó, các chú thích VTS đã phân tích cú pháp sẽ do Trình biên dịch VTS (VTSC) xử lý.
Chú giải sử dụng cú pháp Java: @annotation
hoặc @annotation(value)
hoặc @annotation(id=value, id=value…)
, trong đó giá trị có thể là một biểu thức hằng số, một chuỗi hoặc danh sách các giá trị bên trong {}
, giống như trong Java. Bạn có thể đính kèm nhiều chú giải có cùng tên vào cùng một mục.
Khai báo chuyển tiếp
Trong HIDL, các cấu trúc có thể không được khai báo trước, khiến các loại dữ liệu tự tham chiếu do người dùng xác định không thể thực hiện được (ví dụ: bạn không thể mô tả danh sách liên kết hoặc cây trong HIDL). Hầu hết các HAL hiện có (trước Android 8.x) đều hạn chế sử dụng các nội dung khai báo chuyển tiếp. Bạn có thể xoá các nội dung này bằng cách sắp xếp lại các nội dung khai báo cấu trúc dữ liệu.
Quy tắc hạn chế này cho phép sao chép cấu trúc dữ liệu theo giá trị bằng một bản sao sâu đơn giản, thay vì theo dõi các giá trị con trỏ có thể xuất hiện nhiều lần trong một cấu trúc dữ liệu tự tham chiếu. Nếu cùng một dữ liệu được truyền hai lần, chẳng hạn như với hai tham số phương thức hoặc vec<T>
trỏ đến cùng một dữ liệu, thì hai bản sao riêng biệt sẽ được tạo và phân phối.
Khai báo lồng nhau
HIDL hỗ trợ các nội dung khai báo lồng nhau ở nhiều cấp độ như mong muốn (có một ngoại lệ được ghi chú bên dưới). Ví dụ:
interface IFoo { uint32_t[3][4][5][6] multidimArray; vec<vec<vec<int8_t>>> multidimVector; vec<bool[4]> arrayVec; struct foo { struct bar { uint32_t val; }; bar b; } struct baz { foo f; foo.bar fb; // HIDL uses dots to access nested type names } …
Trường hợp ngoại lệ là các loại giao diện chỉ có thể được nhúng trong vec<T>
và chỉ có một cấp độ sâu (không có vec<vec<IFoo>>
).
Cú pháp con trỏ thô
Ngôn ngữ HIDL không sử dụng * và không hỗ trợ tính linh hoạt đầy đủ của con trỏ thô C/C++. Để biết thông tin chi tiết về cách HIDL đóng gói con trỏ và mảng/vectơ, hãy xem mẫu vec<T>.
Giao diện
Từ khoá interface
có hai cách sử dụng.
- Thao tác này sẽ mở phần khai báo giao diện trong tệp .hal.
- Bạn có thể dùng loại này làm loại đặc biệt trong các trường cấu trúc/hợp nhất, tham số phương thức và giá trị trả về. Đây được xem là một giao diện chung và đồng nghĩa với
android.hidl.base@1.0::IBase
.
Ví dụ: IServiceManager
có phương thức sau:
get(string fqName, string name) generates (interface service);
Phương thức này hứa hẹn sẽ tra cứu một số giao diện theo tên. Cách này cũng giống như cách thay thế giao diện bằng android.hidl.base@1.0::IBase
.
Bạn chỉ có thể truyền giao diện theo hai cách: dưới dạng tham số cấp cao nhất hoặc dưới dạng thành viên của vec<IMyInterface>
. Các vectơ này không thể là thành phần của vectơ, cấu trúc, mảng hoặc tổ hợp lồng nhau.
MQDescriptorSync và MQDescriptorUnsync
Các loại MQDescriptorSync
và MQDescriptorUnsync
truyền các nội dung mô tả Hàng đợi tin nhắn nhanh (FMQ) đồng bộ hoá hoặc không đồng bộ hoá qua giao diện HIDL. Để biết thông tin chi tiết, hãy xem HIDL C++ (FMQ không được hỗ trợ trong Java).
loại bộ nhớ
Loại memory
được dùng để biểu thị bộ nhớ dùng chung chưa được liên kết trong HIDL. Loại này chỉ được hỗ trợ trong C++. Bạn có thể sử dụng giá trị thuộc loại này ở đầu nhận để khởi chạy đối tượng IMemory
, liên kết bộ nhớ và sử dụng được bộ nhớ. Để biết thông tin chi tiết, hãy xem phần HIDL C++.
Cảnh báo: Dữ liệu có cấu trúc được đặt trong bộ nhớ dùng chung PHẢI là loại có định dạng không bao giờ thay đổi trong suốt thời gian hoạt động của phiên bản giao diện truyền memory
. Nếu không, HAL có thể gặp phải các vấn đề nghiêm trọng về khả năng tương thích.
loại con trỏ
Loại pointer
chỉ dành cho mục đích sử dụng nội bộ của HIDL.
Mẫu loại bitfield<T>
bitfield<T>
trong đó T
là một enum do người dùng xác định cho biết giá trị là một OR bit của các giá trị enum được xác định trong T
. Trong mã được tạo, bitfield<T>
xuất hiện dưới dạng loại cơ bản của T. Ví dụ:
enum Flag : uint8_t { HAS_FOO = 1 << 0, HAS_BAR = 1 << 1, HAS_BAZ = 1 << 2 }; typedef bitfield<Flag> Flags; setFlags(Flags flags) generates (bool success);
Trình biên dịch xử lý Cờ loại giống như uint8_t
.
Tại sao không sử dụng (u)int8_t
/(u)int16_t
/(u)int32_t
/(u)int64_t
? Việc sử dụng bitfield
cung cấp thêm thông tin HAL cho người đọc, người đọc hiện đã biết rằng setFlags
lấy giá trị Flag theo bit-OR (tức là biết rằng việc gọi setFlags
bằng 16 là không hợp lệ). Nếu không có bitfield
, thông tin này chỉ được truyền qua tài liệu. Ngoài ra, VTS thực sự có thể kiểm tra xem giá trị của cờ có phải là OR bit của Cờ hay không.
Tay cầm kiểu nguyên gốc
CẢNH BÁO: Địa chỉ thuộc bất kỳ loại nào (ngay cả địa chỉ thiết bị thực) không được nằm trong một tay cầm gốc. Việc truyền thông tin này giữa các quy trình là nguy hiểm và khiến các quy trình dễ bị tấn công. Mọi giá trị được truyền giữa các quy trình đều phải được xác thực trước khi được dùng để truy vấn bộ nhớ được phân bổ trong một quy trình. Nếu không, các tay điều khiển không hợp lệ có thể gây ra tình trạng truy cập bộ nhớ không hợp lệ hoặc hỏng bộ nhớ.
Ngữ nghĩa HIDL là sao chép theo giá trị, nghĩa là các tham số được sao chép.
Mọi phần dữ liệu lớn hoặc dữ liệu cần được chia sẻ giữa các quy trình (chẳng hạn như hàng rào đồng bộ hoá) đều được xử lý bằng cách truyền xung quanh chỉ số mô tả tệp trỏ đến các đối tượng ổn định: ashmem
cho bộ nhớ dùng chung, tệp thực tế hoặc bất kỳ nội dung nào khác có thể ẩn sau chỉ số mô tả tệp. Trình điều khiển liên kết sẽ sao chép chỉ số mô tả tệp vào quy trình khác.
native_handle_t
Android hỗ trợ native_handle_t
, một khái niệm chung về tay cầm được xác định trong libcutils
.
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;
Tay điều khiển gốc là một tập hợp các int và chỉ số mô tả tệp được truyền theo giá trị. Bạn có thể lưu trữ một chỉ số mô tả tệp trong một tay cầm gốc không có int và một chỉ số mô tả tệp. Việc truyền tay cầm bằng tay cầm gốc được đóng gói bằng loại nguyên thuỷ handle
đảm bảo rằng tay cầm gốc được đưa trực tiếp vào HIDL.
Vì native_handle_t
có kích thước biến đổi, nên bạn không thể đưa trực tiếp vào một cấu trúc. Trường handle (tên) tạo một con trỏ đến một native_handle_t
được phân bổ riêng.
Trong các phiên bản Android trước, các tay điều khiển gốc được tạo bằng cách sử dụng cùng một hàm có trong libcutils.
Trong Android 8.0 trở lên, các hàm này hiện được sao chép vào không gian tên android::hardware::hidl
hoặc di chuyển sang NDK. Mã do HIDL tự động tạo sẽ tự động chuyển đổi tuần tự và chuyển đổi tuần tự các hàm này mà không cần đến mã do người dùng viết.
Xử lý và quyền sở hữu chỉ số mô tả tệp
Khi bạn gọi một phương thức giao diện HIDL truyền (hoặc trả về) một đối tượng hidl_handle
(cấp cao nhất hoặc một phần của loại hợp chất), quyền sở hữu của các chỉ số mô tả tệp có trong đó như sau:
- Thực thể gọi truyền đối tượng
hidl_handle
dưới dạng đối số giữ lại quyền sở hữu đối với chỉ số mô tả tệp có trongnative_handle_t
mà đối tượng gọi gói lại; thực thể gọi phải đóng các chỉ số mô tả tệp này khi thực thể gọi hoàn tất việc sử dụng các chỉ số mô tả tệp đó. - Quy trình trả về một đối tượng
hidl_handle
(bằng cách truyền đối tượng đó vào hàm_cb
) giữ lại quyền sở hữu đối với chỉ số mô tả tệp có trongnative_handle_t
do đối tượng bao bọc; quy trình này phải đóng các chỉ số mô tả tệp này khi hoàn tất. - Công cụ truyền tải nhận
hidl_handle
có quyền sở hữu các chỉ số mô tả tệp bên trongnative_handle_t
do đối tượng bao bọc; trình nhận có thể sử dụng các chỉ số mô tả tệp này như trong lệnh gọi lại giao dịch, nhưng phải sao chép tay cầm gốc để sử dụng các chỉ số mô tả tệp ngoài lệnh gọi lại. Phương thức truyền tải sẽ tự động gọiclose()
cho các chỉ số mô tả tệp khi giao dịch hoàn tất.
HIDL không hỗ trợ các tay điều khiển trong Java (vì Java không hỗ trợ các tay điều khiển).
Mảng có kích thước
Đối với các mảng có kích thước trong cấu trúc HIDL, các phần tử của mảng có thể thuộc bất kỳ loại nào mà cấu trúc có thể chứa:
struct foo { uint32_t[3] x; // array is contained in foo };
Chuỗi
Chuỗi xuất hiện khác nhau trong C++ và Java, nhưng loại bộ nhớ truyền tải cơ bản là cấu trúc C++. Để biết thông tin chi tiết, hãy xem phần Loại dữ liệu HIDL C++ hoặc Loại dữ liệu HIDL Java.
Lưu ý: Việc truyền một chuỗi đến hoặc từ Java thông qua giao diện HIDL (bao gồm cả Java đến Java) sẽ khiến các lượt chuyển đổi bộ ký tự có thể không giữ nguyên cách mã hoá ban đầu.
Mẫu loại vec<T>
Mẫu vec<T>
đại diện cho một vùng đệm có kích thước biến chứa các thực thể của T
.
T
có thể là một trong những loại sau:
- Kiểu nguyên thuỷ (ví dụ: uint32_t)
- Chuỗi
- Enum do người dùng xác định
- Cấu trúc do người dùng xác định
- Giao diện hoặc từ khoá
interface
(vec<IFoo>
,vec<interface>
chỉ được hỗ trợ dưới dạng tham số cấp cao nhất) - Tên người dùng
- bitfield<U>
- vec<U>, trong đó U có trong danh sách này ngoại trừ giao diện (ví dụ:
vec<vec<IFoo>>
không được hỗ trợ) - U[] (mảng có kích thước của U), trong đó U nằm trong danh sách này ngoại trừ giao diện
Loại do người dùng xác định
Phần này mô tả các loại do người dùng xác định.
Enum
HIDL không hỗ trợ enum ẩn danh. Nếu không, enum trong HIDL sẽ tương tự như C++11:
enum name : type { enumerator , enumerator = constexpr , … }
Enum cơ sở được xác định theo một trong các loại số nguyên trong HIDL. Nếu bạn không chỉ định giá trị cho bộ đếm đầu tiên của một enum dựa trên loại số nguyên, thì giá trị mặc định sẽ là 0. Nếu bạn không chỉ định giá trị cho một bộ đếm sau này, giá trị mặc định sẽ là giá trị trước đó cộng với một. Ví dụ:
// RED == 0 // BLUE == 4 (GREEN + 1) enum Color : uint32_t { RED, GREEN = 3, BLUE }
Một enum cũng có thể kế thừa từ một enum đã xác định trước đó. Nếu bạn không chỉ định giá trị cho bộ đếm đầu tiên của enum con (trong trường hợp này là FullSpectrumColor
), thì giá trị mặc định sẽ là giá trị của bộ đếm cuối cùng của enum mẹ cộng với một. Ví dụ:
// ULTRAVIOLET == 5 (Color:BLUE + 1) enum FullSpectrumColor : Color { ULTRAVIOLET }
Cảnh báo: Tính năng kế thừa Enum hoạt động ngược lại với hầu hết các loại kế thừa khác. Không thể sử dụng giá trị enum con làm giá trị enum mẹ. Điều này là do enum con chứa nhiều giá trị hơn enum mẹ. Tuy nhiên, bạn có thể sử dụng giá trị enum mẹ một cách an toàn làm giá trị enum con vì theo định nghĩa, giá trị enum con là tập hợp con của giá trị enum mẹ. Hãy lưu ý điều này khi thiết kế giao diện vì điều này có nghĩa là các loại tham chiếu đến enum mẹ không thể tham chiếu đến enum con trong các lần lặp lại sau của giao diện.
Các giá trị của enum được tham chiếu bằng cú pháp dấu hai chấm (không phải cú pháp dấu chấm như các loại lồng nhau). Cú pháp là Type:VALUE_NAME
. Không cần chỉ định loại nếu giá trị được tham chiếu trong cùng một loại enum hoặc các loại con. Ví dụ:
enum Grayscale : uint32_t { BLACK = 0, WHITE = BLACK + 1 }; enum Color : Grayscale { RED = WHITE + 1 }; enum Unrelated : uint32_t { FOO = Color:RED + 1 };
Kể từ Android 10, enum có thuộc tính len
có thể được dùng trong biểu thức hằng.
MyEnum::len
là tổng số mục nhập trong tập hợp liệt kê đó.
Điều này khác với tổng số giá trị, có thể nhỏ hơn khi giá trị bị trùng lặp.
Cấu trúc
HIDL không hỗ trợ các cấu trúc ẩn danh. Nếu không, các cấu trúc trong HIDL rất giống với C.
HIDL không hỗ trợ các cấu trúc dữ liệu có độ dài biến chứa hoàn toàn trong một cấu trúc. Điều này bao gồm mảng có độ dài không xác định đôi khi được dùng làm trường cuối cùng của một cấu trúc trong C/C++ (đôi khi có kích thước là [0]
). HIDL vec<T>
đại diện cho các mảng có kích thước động với dữ liệu được lưu trữ trong một vùng đệm riêng biệt; các thực thể như vậy được biểu thị bằng một thực thể của vec<T>
trong struct
.
Tương tự, string
có thể được chứa trong struct
(các vùng đệm liên kết là riêng biệt). Trong C++ được tạo, các thực thể của loại tay điều khiển HIDL được biểu thị thông qua con trỏ đến tay điều khiển gốc thực tế vì các thực thể của loại dữ liệu cơ bản có độ dài biến đổi.
Union
HIDL không hỗ trợ các liên kết ẩn danh. Nếu không, các tổ hợp sẽ tương tự như C.
Các liên kết không được chứa các loại sửa lỗi (chẳng hạn như con trỏ, chỉ số mô tả tệp, đối tượng liên kết). Các lớp này không cần trường đặc biệt hoặc loại liên kết và chỉ cần sao chép bằng memcpy()
hoặc tương đương. Một liên kết có thể không trực tiếp chứa (hoặc chứa bằng cách sử dụng các cấu trúc dữ liệu khác) bất kỳ nội dung nào yêu cầu đặt độ dời liên kết (tức là tham chiếu giao diện liên kết hoặc xử lý). Ví dụ:
union UnionType { uint32_t a; // vec<uint32_t> r; // Error: can't contain a vec<T> uint8_t b;1 }; fun8(UnionType info); // Legal
Bạn cũng có thể khai báo các liên kết bên trong cấu trúc. Ví dụ:
struct MyStruct { union MyUnion { uint32_t a; uint8_t b; }; // declares type but not member union MyUnion2 { uint32_t a; uint8_t b; } data; // declares type but not member }