Kiểu dữ liệu

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 HIDL C++ (dành cho C++ triển khai nhanh) hoặc HIDL Java (để 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à kết hợp ẩn danh không được hỗ trợ.
  • Typedef được cho phép trong HIDL (vì chúng trong C++).
  • Nhận xét kiểu C++ được cho phép và được sao chép vào tệp tiêu đề đã tạo.

Các đ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++ đã tạo là ::android::hardware::….
  • Tất cả các định nghĩa của tệp đều nằm trong một kiểu Java Trình bao bọc interface.
  • Phần 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
    
  • Nhận xét tương tự như định dạng javadoc.

Trình bày dữ liệu

struct hoặc union bao gồm Bố cục chuẩn (một tập con trong yêu cầu đối với các loại dữ liệu thuần tuý cũ) có bộ nhớ nhất quán bố cục trong mã C++ đã tạo, được thực thi với các thuộc tính căn chỉnh rõ ràng trên structunion thành viên.

Loại HIDL nguyên bản, cũng như enumbitfield loại (luôn bắt nguồn từ các loại nguyên hàm), ánh xạ tới các loại C++ tiêu chuẩn chẳng hạn như std::uint32_t từ cstdint.

Vì Java không hỗ trợ các kiểu chưa có chữ ký, nên các loại HIDL chưa ký sẽ được ánh xạ tới kiểu 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; hợp nhất hiện chưa được hỗ trợ trong Java. Chuỗi được lưu trữ nội bộ dưới dạng UTF8. Vì Java hỗ trợ chỉ các chuỗi UTF16, các giá trị chuỗi được gửi đến hoặc từ quá trình triển khai Java được và có thể không giống hệt nhau trong quá trình dịch lại vì bộ ký tự không giống nhau luôn lập bản đồ suôn sẻ.

Dữ liệu nhận được qua IPC trong C++ được đánh dấu là const và đang ở bộ nhớ chỉ đọc này chỉ tồn tại trong thời gian diễn ra 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, nên công cụ này có thể được giữ lại mà không sao chép thêm (và có thể sửa đổi được).

Chú thích

Bạn có thể thêm chú giải kiểu Java vào phần khai báo kiểu. Chú thích là được phân tích cú pháp bằng phần phụ trợ Bộ kiểm thử nhà cung cấp (VTS) của trình biên dịch HIDL nhưng không có các chú giải được phân tích cú pháp như vậy thực sự được trình biên dịch HIDL hiểu. Thay vào đó, Các chú giải VTS đã phân tích cú pháp được 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 một 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 nội dung khai báo

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 cấu trúc do người dùng xác định loại dữ liệu tự tham chiếu không thể thực hiện được (ví dụ: bạn không thể mô tả một danh sách liên kết hoặc một cây trong HIDL). Hầu hết HAL (trước Android 8.x) hiện có đều hạn chế sử dụng chuyển tiếp khai báo (có thể xoá bằng cách sắp xếp lại cấu trúc dữ liệu) nội dung khai báo.

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 sao chép sâu thay vì theo dõi các giá trị con trỏ có thể xảy ra thời gian 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 2 tham số phương thức hoặc vec<T> trỏ đến cùng một dữ liệu, hai bản sao riêng biệt sẽ được tạo và gửi đ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 độ tuỳ thích (trong đó có một cấp độ ngoại lệ được nêu 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ỉ sâu 1 cấp (không vec<vec<IFoo>>).

Cú pháp con trỏ thô

Ngôn ngữ HIDL không sử dụng * và không hỗ trợ có tính linh hoạt tối đa 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 vec<T> mẫu.

Giao diện

Từ khoá interface có hai cách sử dụng.

  • Thao tác này sẽ mở định nghĩa về một giao diện trong tệp .hal.
  • Hàm này có thể được dùng như một loại đặc biệt trong các trường cấu trúc/kết hợp, tham số phương thức, và lợi nhuận. Giao diện được coi là một giao diện chung và từ đồ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. Ngoài ra giống hệt để thay thế giao diện bằng android.hidl.base@1.0::IBase.

Giao diện chỉ có thể được truyền theo hai cách: dưới dạng thông số cấp cao nhất hoặc dưới dạng thành viên của vec<IMyInterface>. Họ không thể là thành viên của vecs, cấu trúc, mảng hoặc phép hợp nhất lồng nhau.

MQDescriptorSync và MQDescriptorUnsync

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

loại bộ nhớ

Loại memory dùng để biểu thị bộ nhớ dùng chung chưa được liên kết trong Phóng điện cường độ cao. Chỉ được hỗ trợ trong C++. Bạn có thể dùng giá trị thuộc loại này trên điểm cuối nhận để khởi tạo đối tượng IMemory, ánh xạ bộ nhớ và làm cho ứng dụng trở nên hữu dụng. Để biết thông tin chi tiết, hãy xem HIDL C++.

Cảnh báo: Dữ liệu có cấu trúc được đặt trong phần được chia sẻ bộ nhớ 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ể bị ảnh hưởng 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 HIDL nội bộ.

trường bit<T> loại mẫu

bitfield<T> trong đó T là một Enum do người dùng xác định cho thấy giá trị là một 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 kiểu 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ý loại Cờ 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 sẽ cung cấp thêm thông tin HAL cho trình đọc, những người hiện biết rằng setFlags nhận giá trị bitwise-OR của Cờ (tức là biết rằng việc gọi setFlags bằng 16 là không hợp lệ). Không có bitfield, thông tin này chỉ được truyền đạt qua tài liệu. Ngang bằng Ngoài ra, VTS thực sự có thể kiểm tra xem giá trị của cờ có phải là bitwise OR của Cờ hay không.

Tên người dùng kiểu gốc

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 quy trình đều phải được xác thực trước khi chúng được dùng để tra cứu bộ nhớ được phân bổ trong một quy 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ớ.

Ngữ nghĩa HIDL là bản sao theo giá trị, ngụ ý rằng các tham số được sao chép. Bất kỳ mảng dữ liệu lớn hoặc dữ liệu nào cần được chia sẻ giữa các quy trình (chẳng hạn như hàng rào đồng bộ hoá) được xử lý bằng cách truyền chỉ số mô tả tệp vào các đối tượng cố định: ashmem để dùng bộ nhớ dùng chung, tệp thực 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 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ề tên người dùng đượ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;

Tên người dùng gốc là một tập hợp số nguyên và chỉ số mô tả tệp được truyền theo giá trị. Chỉ một chỉ số mô tả tệp duy nhất có thể được lưu trữ trong một tên người dùng gốc có không có int và một chỉ số mô tả tệp duy nhất. Truyền dữ liệu xử lý bằng các ô điều khiển gốc được đóng gói bằng loại nguyên hàm handle đảm bảo rằng mã gốc tên người dùng được đưa trực tiếp vào HIDL.

native_handle_t có kích thước thay đổi nên không thể bao gồm được trực tiếp trong một cấu trúc. Trường xử lý tạo ra một con trỏ đến một thuộc tính riêng biệt đã phân bổ native_handle_t.

Trong các phiên bản Android trước đây, các tên người dùng gốc được tạo bằ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 đã chuyển sang NDK. Phóng điện cường độ cao mã tạo tự động sẽ tự động chuyển đổi tuần tự và giải tuần tự các hàm này, mà không cần tham gia vào mã do người dùng viết.

Quyền sở hữu tên người dùng và 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 phức hợp), quyền sở hữu của chỉ số mô tả tệp có trong thư như sau:

  • Phương thức gọi truyền đối tượng hidl_handle dưới dạng một đối số vẫn giữ quyền sở hữu của chỉ số mô tả tệp có trong native_handle_t sẽ gói; phương thức gọi phải đóng các tệp này khi thực hiện với chúng.
  • Quy trình trả về một hidl_handle (bằng cách chuyển đối tượng này vào hàm _cb) giữ quyền sở hữu đối tượng chỉ số mô tả tệp có trong native_handle_t được bao bọc bởi đối tượng; bạn phải đóng các chỉ số mô tả tệp này khi thực hiện xong.
  • Một phương tiện vận tải nhận được hidl_handle có quyền sở hữu của chỉ số mô tả tệp trong native_handle_t được bao bọc bởi đối tượng; bộ 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 xử lý gốc để sử dụng tệp ngoài lệnh gọi lại. Phương tiện giao thông tự động gọi close() cho các chỉ số mô tả tệp khi giao dịch hoàn tất.

HIDL không hỗ trợ tên người dùng trong Java (vì Java không hỗ trợ tên người dùng tại tất cả).

Mảng được định 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ỳ kiểu cấu trúc nào có thể chứa:

struct foo {
uint32_t[3] x; // array is contained in foo
};

Chuỗi

Các chuỗi xuất hiện khác nhau trong C++ và Java, nhưng là phương thức truyền tải cơ bản loại bộ nhớ là một cấu trúc C++. Để biết thông tin chi tiết, hãy xem Loại dữ liệu HiDL C++ hoặc Các loại dữ liệu Java HiDL.

Lưu ý: Việc chuyển một chuỗi đến hoặc từ Java thông qua một Giao diện HIDL (bao gồm cả Java sang Java) gây ra chuyển đổi bộ ký tự có thể không giữ được chế độ mã hoá ban đầu.

vec<T> loại mẫu

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

T có thể là một trong những trạng thái sau:

  • Loại gốc (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> được hỗ trợ chỉ là thông số cấp cao nhất)
  • Tên người dùng
  • 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 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.

Liệt kê

HIDL không hỗ trợ enum ẩn danh. Nếu không, các giá trị enum trong HIDL cũng tương tự nhau lên 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 giá trị được chỉ định cho liệt kê đầu tiên của một enum dựa trên số nguyên loại, giá trị được mặc định là 0. Nếu không có giá trị nào được chỉ định cho một liệt kê sau này, giá trị mặc định là giá trị trước đó cộng 1. 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 không có giá trị nào là được chỉ định cho giá trị enum đầu tiên của một giá trị enum con (trong trường hợp này FullSpectrumColor), mặc định là giá trị của biến thể gần đây nhất enum của enum mẹ cộng một. Ví dụ:

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

Cảnh báo: Tính kế thừa Enum hoạt động ngược từ hầu hết các loại kế thừa khác. Bạn không thể sử dụng giá trị enum con làm giá trị giá trị enum mẹ. Điều này là do một enum con bao gồm nhiều giá trị hơn giá trị cha mẹ. Tuy nhiên, bạn có thể dùng giá trị enum mẹ làm giá trị enum con một cách an toàn vì theo định nghĩa, giá trị enum con là tập mẹ của các 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 .

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 ghép). 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 kiểu 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ể dùng trong biểu thức không đổi. MyEnum::len là tổng số mục nhập trong giá trị liệt kê đó. Giá trị này khác với tổng số giá trị. Giá trị này có thể nhỏ hơn khi 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 tương tự như C.

HIDL không hỗ trợ các cấu trúc dữ liệu có độ dài biến hoàn toàn nằm trong một cấu trúc. Dữ liệu này bao gồm mảng có độ dài vô hạn mà đôi khi được dùng như trường cuối cùng của một cấu trúc trong C/C++ (đôi khi được nhìn thấy với kích thước [0]). HIDL vec<T> biểu thị kích thước động các mảng có dữ liệu được lưu trữ trong một bộ đệm riêng; những trường hợp 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ể nằm trong struct (các vùng đệm liên kết thì riêng biệt). Trong C++ đã tạo, các thực thể của HIDL kiểu xử lý được biểu thị thông qua một con trỏ đến xử lý gốc thực tế dưới dạng các bản sao của loại dữ liệu cơ bản có độ dài biến thiên.

Union

HIDL không hỗ trợ liên kết ẩn danh. Nếu không, phép hợp nhất sẽ tương tự như C.

Liên kết không được chứa các loại bản sửa lỗi (chẳng hạn như con trỏ, chỉ số mô tả tệp, liên kết đối tượng). Chúng không cần các trường đặc biệt hoặc các loại liên kết và chỉ sao chép bằng memcpy() hoặc loại tương đương. Hợp nhấ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 cài đặt độ lệch liên kết (tức là tham chiếu ô điều khiển hoặc giao diện liên kết). 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

Liên kết cũng có thể được khai báo 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
  }