Kiểu dữ liệu

Phần này mô tả các loại dữ liệu HIDL. Để biết chi tiết triển khai, hãy xem HIDL C++ (đối với triển khai C++) hoặc HIDL Java (đối với triển khai Java).

Những điểm tương đồng với C++ bao gồm:

  • 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ấu trúc và công đoàn ẩn danh không được hỗ trợ.
  • Typedef được cho phép trong HIDL (giống như trong 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.

Những điểm tương đồng với Java bao gồm:

  • Đố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 được chứa trong trình bao bọc interface kiểu Java.
  • Khai báo mảng HIDL tuân theo kiểu Java, không phải kiểu C++. Ví dụ:
    struct Point {
        int32_t x;
        int32_t y;
    };
    Point[3] triangle;   // sized array
    
  • Nhận xét tương tự như định dạng javadoc.

Sự miêu tả dữ liệu

Một struct hoặc union bao gồm Bố cục tiêu chuẩn (một tập hợp con yêu cầu của các kiểu 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 viên structunion .

Các loại HIDL nguyên thủy, cũng như các loại enumbitfield (luôn xuất phát từ các loại nguyên thủy), ánh xạ tới các loại C++ tiêu chuẩn như std::uint32_t từ cstdint .

Vì Java không hỗ trợ các kiểu không dấu, nên các kiểu HIDL không dấu được ánh xạ tới kiểu Java đã ký tương ứng. Ánh xạ cấu trúc tới các lớp Java; mảng ánh xạ tới mảng Java; công đoàn 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ợ các chuỗi UTF16 nên các giá trị chuỗi được gửi đến hoặc 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ạ trơn tru.

Dữ liệu nhận được qua IPC trong C++ được đánh dấu const và nằm trong bộ nhớ chỉ đọc và chỉ tồn tại trong suốt thời gian của lệnh 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, do đó nó 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

Các chú thích kiểu Java có thể được thêm vào các khai báo kiểu. Các chú thích được phân tích cú pháp bởi phần phụ trợ Vendor Test Suite (VTS) của trình biên dịch HIDL nhưng không có chú thích nào được phân tích cú pháp như vậy thực sự được trình biên dịch HIDL hiểu được. Thay vào đó, các chú thích VTS được phân tích cú pháp được xử lý bởi Trình biên dịch VTS (VTSC).

Chú thích 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à biểu thức hằng số, chuỗi hoặc danh sách các giá trị bên trong {} , giống như trong Java. Nhiều chú thích có cùng tên có thể được đính kèm vào cùng một mục.

Chuyển tiếp tờ khai

Trong HIDL, các cấu trúc có thể không được khai báo chuyển tiếp, khiến cho các kiểu dữ liệu tự tham chiếu, do người dùng xác định là không thể (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 khai báo chuyển tiếp, có thể loại bỏ bằng cách sắp xếp lại các khai báo cấu trúc dữ liệu.

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 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> s 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 khai báo lồng nhau ở nhiều mức độ mong muốn (với 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
    }
    …

Ngoại lệ là các loại giao diện chỉ có thể được nhúng trong vec<T> và chỉ sâu một cấp (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 hoàn toàn của con trỏ thô C/C++. Để biết 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ừ khóa interface có hai cách sử dụng.

  • Nó mở định nghĩa của giao diện trong tệp .hal.
  • Nó có thể được sử dụng như một loại đặc biệt trong các trường struct/union, tham số phương thức và trả về. Nó được xem như 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 pháp này hứa hẹn sẽ tra cứu một số giao diện theo tên. Việc thay thế giao diện bằng android.hidl.base@1.0::IBase cũng tương tự.

Các giao diện chỉ có thể được truyề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> . Chúng không thể là thành viên của các vec, cấu trúc, mảng hoặc liên kết lồng nhau.

MQDescriptorSync & MQDescriptorUnsync

Các loại MQDescriptorSyncMQDescriptorUnsync chuyển một bộ mô tả Hàng đợi tin nhắn nhanh (FMQ) được đồng bộ hóa hoặc không đồng bộ hóa trên giao diện HIDL. Để biết chi tiết, hãy xem HIDL C++ (FMQ không được hỗ trợ trong Java).

loại bộ nhớ

Loại bộ memory được sử dụng để thể hiện bộ nhớ chia sẻ chưa được ánh xạ trong HIDL. Nó chỉ được hỗ trợ trong C++. Giá trị thuộc loại này có thể được sử dụng ở đầu nhận để khởi tạo đối tượng IMemory , ánh xạ bộ nhớ và làm cho nó có thể sử dụng được. Để biết chi tiết, xem 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 tồn tại của phiên bản giao diện truyền qua memory . Nếu không, HAL có thể gặp phải các vấn đề tương thích nghiêm trọng.

kiểu con trỏ

Loại pointer chỉ dành cho sử dụng nội bộ HIDL.

mẫu bitfield<T>

bitfield<T> trong đó Tenum do người dùng xác định gợi ý giá trị là bitwise-OR 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ý kiểu Flags 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ông tin HAL bổ sung cho người đọc, người bây giờ biết rằng setFlags nhận giá trị bitwise-OR của Flag (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 tải 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à bit-OR của Cờ hay không.

xử lý kiểu nguyên thủy

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 chúng đượ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ớ.

Ngữ nghĩa HIDL là sao chép theo giá trị, ngụ ý rằng các tham số được sao chép. Bất kỳ phần dữ liệu lớn nào 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ộ hóa), đều được xử lý bằng cách chuyển xung quanh các bộ mô tả tệp trỏ đến các đối tượng liên tục: ashmem cho bộ nhớ dùng chung, tệp thực hoặc bất kỳ thứ gì khác có thể ẩn đằng sau một bộ mô tả tập tin. Trình điều khiển liên kết sao chép bộ mô tả tệp vào quy trình khác.

bản địa_handle_t

Android hỗ trợ native_handle_t , một khái niệm xử lý chung đượ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;

Một mã điều khiển gốc là một tập hợp các mô tả int và tệp được truyền theo giá trị. Một bộ mô tả tệp duy nhất có thể được lưu trữ trong một bộ xử lý gốc không có int và một bộ mô tả tệp duy nhất. Việc chuyển các thẻ điều khiển bằng cách sử dụng các thẻ handle gốc được đóng gói bằng kiểu xử lý nguyên thủy đảm bảo rằng các thẻ điều khiển gốc được đưa trực tiếp vào HIDL.

native_handle_t có kích thước thay đổi nên nó không thể được đưa trực tiếp vào cấu trúc. Trường xử lý tạo một con trỏ tới một native_handle_t được phân bổ riêng.

Trong các phiên bản Android trước, các thẻ điều khiển gốc được tạo bằng cách sử dụng các chức năng tương tự có trong libcutils . Trong Android 8.0 trở lên, các chức năng này hiện được sao chép vào không gian tên android::hardware::hidl hoặc được chuyển sang NDK. Mã được tạo tự động HIDL sẽ tự động tuần tự hóa và giải tuần tự hóa các hàm này mà không cần sự tham gia của mã do người dùng viết.

Xử lý và sở hữu bộ mô tả tập tin

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 kết hợp), quyền sở hữu của các bộ mô tả tệp có trong đó như sau:

  • Người gọi truyền một đối tượng hidl_handle làm đối số vẫn giữ quyền sở hữu các bộ mô tả tệp có trong native_handle_t mà nó bao bọc; người gọi phải đóng các bộ mô tả tệp này khi hoàn tất.
  • Quá trình trả về một đối tượng hidl_handle (bằng cách chuyển nó vào hàm _cb ) vẫn giữ quyền sở hữu các bộ mô tả tệp có trong native_handle_t được đối tượng bao bọc; quá trình phải đóng các bộ mô tả tệp này khi hoàn tất.
  • Phương tiện vận chuyển nhận hidl_handle có quyền sở hữu các bộ mô tả tệp bên trong native_handle_t được đối tượng bao bọc; người nhận có thể sử dụng nguyên các bộ mô tả tệp này trong quá trình gọi lại giao dịch, nhưng phải sao chép phần xử lý gốc để sử dụng các bộ mô tả tệp ngoài lệnh gọi lại. Việc vận chuyển sẽ tự động close() các bộ mô tả tệp khi giao dịch được thực hiện.

HIDL không hỗ trợ các thẻ điều khiển trong Java (vì Java hoàn toàn không hỗ trợ các thẻ đ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 chú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
};

Dây

Các chuỗi xuất hiện khác nhau trong C++ và Java, nhưng kiểu lưu trữ truyền tải cơ bản là cấu trúc C++. Để biết chi tiết, hãy xem Kiểu dữ liệu HIDL C++ hoặc Kiểu 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 Java sang Java) sẽ gây ra các chuyển đổi bộ ký tự có thể không bảo toàn chính xác mã hóa gốc.

mẫu loại vec<T>

Mẫu vec<T> đại diện cho một bộ đệm có kích thước thay đổi chứa các phiên bản của T .

T có thể là một trong những điều sau đây:

  • Các kiểu nguyên thủy (ví dụ uint32_t)
  • Dây
  • Enum do người dùng định nghĩa
  • Cấu trúc do người dùng định nghĩa
  • Giao diện hoặc từ khóa interface ( vec<IFoo> , vec<interface> chỉ được hỗ trợ dưới dạng tham số cấp cao nhất)
  • Tay cầm
  • trường bit<U>
  • vec<U>, trong đó U nằm 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

Các 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.

liệt kê

HIDL không hỗ trợ enum ẩn danh. Mặt khác, enum trong HIDL tương tự như C++11:

enum name : type { enumerator , enumerator = constexpr , …  }

Một enum cơ sở được định nghĩa theo một trong các kiểu số nguyên trong HIDL. Nếu không có giá trị nào được chỉ định cho điều tra viên đầu tiên của enum dựa trên loại số nguyên, thì giá trị mặc định là 0. Nếu không có giá trị nào được chỉ định cho điều tra viên sau, giá trị mặc định 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 được xác định trước đó. Nếu không có giá trị nào được chỉ định cho điều tra viên đầu tiên của enum con (trong trường hợp này FullSpectrumColor ), thì giá trị mặc định là giá trị của điều tra viên cuối cùng của enum cha cộng với một. Ví dụ:

// ULTRAVIOLET == 5 (Color:BLUE + 1)
enum FullSpectrumColor : Color { ULTRAVIOLET }

Cảnh báo: Kế thừa Enum hoạt động ngược với hầu hết các kiểu kế thừa khác. Giá trị enum con không thể được sử dụng làm giá trị enum cha. Điều này là do enum con bao gồm nhiều giá trị hơn enum cha. Tuy nhiên, giá trị enum cha có thể được sử dụng một cách an toàn làm giá trị enum con vì giá trị enum con theo định nghĩa là tập hợp siêu của giá trị enum cha. Hãy ghi nhớ điều này khi thiết kế giao diện vì điều này có nghĩa là các kiểu tham chiếu đến enum cha không thể tham chiếu đến enum con trong các lần lặp lại giao diện sau này của bạn.

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ư kiểu 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 loại enum hoặ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 };

Bắt đầu từ Android 10, enum có thuộc tính len có thể được sử dụng trong biểu thức hằng số. MyEnum::len là tổng số mục trong bảng liệt kê đó. Giá trị này khác với tổng số giá trị, có thể nhỏ hơn khi các giá trị bị trùng lặp.

Cấu trúc

HIDL không hỗ trợ cấu trúc ẩn danh. Mặt khác, 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 thay đổi được 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 sử dụng làm trường cuối cùng của cấu trúc trong C/C++ (đôi khi được thấy với kích thước [0] ). HIDL vec<T> biểu thị các mảng có kích thước động với dữ liệu được lưu trữ trong một bộ đệm riêng biệt; những trường hợp như vậy được biểu diễn bằng một phiên bản của vec<T> trong struct .

Tương tự, string có thể được chứa trong một struct (các bộ đệm liên quan là riêng biệt). Trong C++ được tạo, các phiên bản của loại trình điều khiển HIDL được biểu diễn thông qua một con trỏ tới trình điều khiển gốc thực tế vì các phiên bản của kiểu dữ liệu cơ bản có độ dài thay đổi.

liên hiệp

HIDL không hỗ trợ các công đoàn ẩn danh. Ngược lại, các công đoàn tương tự như C.

Các hiệp hội không thể chứa các loại sửa lỗi (con trỏ, bộ mô tả tệp, đối tượng liên kết, v.v.). Chúng không cần các trường đặc biệt hoặc các loại liên quan và chỉ được sao chép thông qua memcpy() hoặc tương đương. Một liên minh có thể không trực tiếp chứa (hoặc chứa thông qua các cấu trúc dữ liệu khác) bất cứ thứ gì yêu cầu thiết lập các offset của chất kết dính (tức là các tham chiếu xử lý hoặc giao diện chất kết dính). 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

Union cũng có thể được khai báo bên trong struct. 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
  }