Bagian ini menjelaskan tipe data HIDL. Untuk detail implementasi, lihat HIDL C++ (untuk implementasi C++) atau HIDL Java (untuk implementasi Java).
Persamaan dengan C++ antara lain:
-
structs
menggunakan sintaks C++;unions
mendukung sintaks C++ secara default. Keduanya harus diberi nama; struct dan serikat anonim tidak didukung. - Typedef diperbolehkan di HIDL (seperti di C++).
- Komentar gaya C++ diperbolehkan dan disalin ke file header yang dihasilkan.
Kemiripannya dengan Java antara lain:
- Untuk setiap file, HIDL mendefinisikan namespace bergaya Java yang harus dimulai dengan
android.hardware.
. Namespace C++ yang dihasilkan adalah::android::hardware::…
. - Semua definisi file terkandung dalam pembungkus
interface
bergaya Java. - Deklarasi array HIDL mengikuti gaya Java, bukan gaya C++. Contoh:
struct Point { int32_t x; int32_t y; }; Point[3] triangle; // sized array
- Komentar mirip dengan format javadoc.
Representasi data
Sebuah struct
atau union
yang terdiri dari Tata Letak Standar (bagian dari persyaratan tipe data lama biasa) memiliki tata letak memori yang konsisten dalam kode C++ yang dihasilkan, diterapkan dengan atribut penyelarasan eksplisit pada struct
dan anggota union
.
Tipe HIDL primitif, serta tipe enum
dan bitfield
(yang selalu berasal dari tipe primitif), dipetakan ke tipe C++ standar seperti std::uint32_t
dari cstdint .
Karena Java tidak mendukung tipe unsigned, tipe HIDL unsigned dipetakan ke tipe Java bertanda tangan yang sesuai. Struktur dipetakan ke kelas Java; array dipetakan ke array Java; serikat pekerja saat ini tidak didukung di Jawa. String disimpan secara internal sebagai UTF8. Karena Java hanya mendukung string UTF16, nilai string yang dikirim ke atau dari implementasi Java akan diterjemahkan, dan mungkin tidak sama saat diterjemahkan ulang karena rangkaian karakter tidak selalu dipetakan dengan lancar.
Data yang diterima melalui IPC di C++ ditandai const
dan berada dalam memori read-only yang hanya bertahan selama durasi pemanggilan fungsi. Data yang diterima melalui IPC di Java telah disalin ke objek Java, sehingga dapat disimpan tanpa penyalinan tambahan (dan dapat dimodifikasi).
Anotasi
Anotasi gaya Java dapat ditambahkan ke deklarasi tipe. Anotasi diurai oleh backend Vendor Test Suite (VTS) dari kompiler HIDL tetapi tidak satu pun dari anotasi yang diurai tersebut benar-benar dipahami oleh kompiler HIDL. Sebaliknya, anotasi VTS yang diurai ditangani oleh VTS Compiler (VTSC).
Anotasi menggunakan sintaksis Java: @annotation
atau @annotation(value)
atau @annotation(id=value, id=value…)
dengan nilai yang dapat berupa ekspresi konstan, string, atau daftar nilai di dalam {}
, sama seperti pada Jawa. Beberapa anotasi dengan nama yang sama dapat dilampirkan ke item yang sama.
Deklarasi ke depan
Di HIDL, struct mungkin tidak dideklarasikan ke depan, sehingga membuat tipe data referensi mandiri yang ditentukan pengguna tidak mungkin dilakukan (misalnya, Anda tidak bisa mendeskripsikan daftar tertaut atau pohon di HIDL). Sebagian besar HAL yang ada (sebelum Android 8.x) memiliki penggunaan deklarasi penerusan yang terbatas, yang dapat dihilangkan dengan mengatur ulang deklarasi struktur data.
Pembatasan ini memungkinkan struktur data untuk disalin berdasarkan nilai dengan salinan dalam yang sederhana, daripada melacak nilai penunjuk yang mungkin terjadi beberapa kali dalam struktur data referensial mandiri. Jika data yang sama diteruskan dua kali, misalnya dengan dua parameter metode atau vec<T>
yang menunjuk ke data yang sama, dua salinan terpisah akan dibuat dan dikirimkan.
Deklarasi bersarang
HIDL mendukung deklarasi bertingkat sebanyak yang diinginkan (dengan satu pengecualian di bawah). Misalnya:
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 } …
Pengecualiannya adalah tipe antarmuka hanya dapat disematkan di vec<T>
dan kedalamannya hanya satu level (tidak ada vec<vec<IFoo>>
).
Sintaks penunjuk mentah
Bahasa HIDL tidak menggunakan * dan tidak mendukung fleksibilitas penuh dari pointer mentah C/C++. Untuk detail tentang cara HIDL merangkum pointer dan array/vektor, lihat vec<T> template .
Antarmuka
Kata kunci interface
memiliki dua penggunaan.
- Ini membuka definisi antarmuka dalam file .hal.
- Ini dapat digunakan sebagai tipe khusus di bidang struct/union, parameter metode, dan pengembalian. Ini dipandang sebagai antarmuka umum dan sinonim dengan
android.hidl.base@1.0::IBase
.
Misalnya, IServiceManager
memiliki metode berikut:
get(string fqName, string name) generates (interface service);
Metode ini menjanjikan untuk mencari beberapa antarmuka berdasarkan nama. Hal ini juga identik dengan mengganti antarmuka dengan android.hidl.base@1.0::IBase
.
Antarmuka hanya dapat diteruskan dengan dua cara: sebagai parameter tingkat atas, atau sebagai anggota vec<IMyInterface>
. Mereka tidak bisa menjadi anggota vec, struct, array, atau gabungan yang disarangkan.
MQDescriptorSync & MQDescriptorUnsync
Tipe MQDescriptorSync
dan MQDescriptorUnsync
meneruskan deskriptor Fast Message Queue (FMQ) yang disinkronkan atau tidak disinkronkan di seluruh antarmuka HIDL. Untuk detailnya, lihat HIDL C++ (FMQ tidak didukung di Java).
tipe memori
Tipe memory
digunakan untuk mewakili memori bersama yang belum dipetakan di HIDL. Ini hanya didukung di C++. Nilai jenis ini dapat digunakan pada pihak penerima untuk menginisialisasi objek IMemory
, memetakan memori dan membuatnya dapat digunakan. Untuk detailnya, lihat HIDL C++ .
Peringatan: Data terstruktur yang ditempatkan di memori bersama HARUS merupakan jenis yang formatnya tidak akan pernah berubah selama masa pakai versi antarmuka yang melewati memory
tersebut. Jika tidak, HAL mungkin mengalami masalah kompatibilitas yang fatal.
tipe penunjuk
Tipe pointer
hanya untuk penggunaan internal HIDL.
templat tipe bitfield<T>
bitfield<T>
di mana T
adalah enum yang ditentukan pengguna menunjukkan bahwa nilainya adalah bitwise-OR dari nilai enum yang ditentukan dalam T
. Dalam kode yang dihasilkan, bitfield<T>
muncul sebagai tipe dasar T. Misalnya:
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);
Kompiler menangani tipe Flags sama seperti uint8_t
.
Mengapa tidak menggunakan (u)int8_t
/ (u)int16_t
/ (u)int32_t
/ (u)int64_t
? Menggunakan bitfield
memberikan informasi HAL tambahan kepada pembaca, yang sekarang mengetahui bahwa setFlags
mengambil nilai Flag bitwise-OR (yaitu mengetahui bahwa memanggil setFlags
dengan 16 tidak valid). Tanpa bitfield
, informasi ini disampaikan hanya melalui dokumentasi. Selain itu, VTS sebenarnya dapat memeriksa apakah nilai flag adalah bitwise-OR dari Flag.
menangani tipe primitif
PERINGATAN: Alamat apa pun (bahkan alamat perangkat fisik) tidak boleh menjadi bagian dari nama asli. Melewati informasi ini antar proses berbahaya dan membuatnya rentan terhadap serangan. Nilai apa pun yang diteruskan antar proses harus divalidasi sebelum digunakan untuk mencari alokasi memori dalam suatu proses. Jika tidak, penanganan yang buruk dapat menyebabkan akses memori buruk atau kerusakan memori.
Semantik HIDL adalah salinan demi nilai, yang menyiratkan bahwa parameter disalin. Setiap potongan data berukuran besar, atau data yang perlu dibagi antar proses (seperti pagar sinkronisasi), ditangani dengan meneruskan deskriptor file yang menunjuk ke objek persisten: ashmem
untuk memori bersama, file sebenarnya, atau apa pun yang dapat bersembunyi di baliknya. deskriptor file. Pengandar pengikat menduplikasi deskriptor file ke dalam proses lainnya.
pegangan_asli_t
Android mendukung native_handle_t
, konsep pegangan umum yang didefinisikan dalam 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;
Pegangan asli adalah kumpulan int dan deskriptor file yang diteruskan berdasarkan nilai. Deskriptor file tunggal dapat disimpan dalam pegangan asli tanpa int dan deskriptor file tunggal. Meneruskan pegangan menggunakan pegangan asli yang dienkapsulasi dengan tipe handle
primitif memastikan bahwa pegangan asli disertakan secara langsung dalam HIDL.
Karena native_handle_t
memiliki ukuran variabel, ia tidak dapat dimasukkan secara langsung ke dalam struct. Bidang pegangan menghasilkan penunjuk ke native_handle_t
yang dialokasikan secara terpisah.
Di versi Android sebelumnya, pengendali asli dibuat menggunakan fungsi yang sama seperti yang ada di libcutils . Di Android 8.0 dan yang lebih tinggi, fungsi-fungsi ini sekarang disalin ke namespace android::hardware::hidl
atau dipindahkan ke NDK. Kode yang dibuat secara otomatis HIDL membuat serialisasi dan deserialisasi fungsi-fungsi ini secara otomatis, tanpa keterlibatan kode yang ditulis pengguna.
Tangani dan kepemilikan deskriptor file
Saat Anda memanggil metode antarmuka HIDL yang meneruskan (atau mengembalikan) objek hidl_handle
(baik tingkat atas atau bagian dari tipe gabungan), kepemilikan deskriptor file yang terdapat di dalamnya adalah sebagai berikut:
- Penelepon yang meneruskan objek
hidl_handle
sebagai argumen mempertahankan kepemilikan deskriptor file yang terdapat dalamnative_handle_t
yang dibungkusnya; penelepon harus menutup deskriptor file ini setelah selesai. - Proses mengembalikan objek
hidl_handle
(dengan meneruskannya ke fungsi_cb
) mempertahankan kepemilikan deskriptor file yang terdapat dalamnative_handle_t
yang dibungkus oleh objek; prosesnya harus menutup deskriptor file ini setelah selesai. - Transportasi yang menerima
hidl_handle
memiliki kepemilikan deskriptor file di dalamnative_handle_t
yang dibungkus oleh objek; penerima dapat menggunakan deskriptor file ini sebagaimana adanya selama callback transaksi, namun harus mengkloning pegangan asli untuk menggunakan deskriptor file di luar callback. Transportasi akan secara otomatisclose()
deskriptor file ketika transaksi selesai.
HIDL tidak mendukung pegangan di Java (karena Java tidak mendukung pegangan sama sekali).
Array berukuran
Untuk array berukuran dalam struct HIDL, elemennya dapat berupa jenis apa pun yang dapat dikandung oleh struct:
struct foo { uint32_t[3] x; // array is contained in foo };
string
String muncul secara berbeda di C++ dan Java, namun tipe penyimpanan transport yang mendasarinya adalah struktur C++. Untuk detailnya, lihat Tipe Data HIDL C++ atau Tipe Data Java HIDL .
Catatan: Melewati string ke atau dari Java melalui antarmuka HIDL (termasuk Java ke Java) akan menyebabkan konversi kumpulan karakter yang mungkin tidak mempertahankan pengkodean aslinya.
templat tipe vec<T>
Templat vec<T>
mewakili buffer berukuran variabel yang berisi instance T
.
T
dapat berupa salah satu dari berikut ini:
- Tipe primitif (misalnya uint32_t)
- string
- Enum yang ditentukan pengguna
- Struktur yang ditentukan pengguna
- Antarmuka, atau kata kunci
interface
(vec<IFoo>
,vec<interface>
hanya didukung sebagai parameter tingkat atas) - Menangani
- bidang bit<U>
- vec<U>, di mana U ada dalam daftar ini kecuali antarmuka (misalnya
vec<vec<IFoo>>
tidak didukung) - U[] (array berukuran U), di mana U ada dalam daftar ini kecuali antarmuka
Tipe yang ditentukan pengguna
Bagian ini menjelaskan tipe yang ditentukan pengguna.
enum
HIDL tidak mendukung enum anonim. Jika tidak, enum di HIDL mirip dengan C++11:
enum name : type { enumerator , enumerator = constexpr , … }
Enum dasar didefinisikan dalam salah satu tipe integer di HIDL. Jika tidak ada nilai yang ditentukan untuk enumerator pertama dari enum berdasarkan tipe integer, nilai defaultnya adalah 0. Jika tidak ada nilai yang ditentukan untuk enumerator selanjutnya, nilai defaultnya adalah nilai sebelumnya ditambah satu. Misalnya:
// RED == 0 // BLUE == 4 (GREEN + 1) enum Color : uint32_t { RED, GREEN = 3, BLUE }
Enum juga dapat mewarisi dari enum yang ditentukan sebelumnya. Jika tidak ada nilai yang ditentukan untuk enumerator pertama dari enum anak (dalam hal ini FullSpectrumColor
), nilai defaultnya adalah nilai enumerator terakhir dari enum induk ditambah satu. Misalnya:
// ULTRAVIOLET == 5 (Color:BLUE + 1) enum FullSpectrumColor : Color { ULTRAVIOLET }
Peringatan: Warisan Enum bekerja mundur dari sebagian besar jenis warisan lainnya. Nilai enum anak tidak dapat digunakan sebagai nilai enum induk. Ini karena enum anak menyertakan lebih banyak nilai daripada enum induknya. Namun, nilai enum induk dapat digunakan dengan aman sebagai nilai enum anak karena nilai enum anak menurut definisi merupakan superset dari nilai enum induk. Ingatlah hal ini ketika mendesain antarmuka karena ini berarti tipe yang mengacu pada enum induk tidak dapat merujuk ke enum anak pada iterasi antarmuka Anda selanjutnya.
Nilai enum dirujuk dengan sintaks titik dua (bukan sintaksis titik sebagai tipe bersarang). Sintaksnya adalah Type:VALUE_NAME
. Tidak perlu menentukan tipe jika nilai direferensikan dalam tipe enum atau tipe anak yang sama. Contoh:
enum Grayscale : uint32_t { BLACK = 0, WHITE = BLACK + 1 }; enum Color : Grayscale { RED = WHITE + 1 }; enum Unrelated : uint32_t { FOO = Color:RED + 1 };
Mulai Android 10, enum memiliki atribut len
yang dapat digunakan dalam ekspresi konstan. MyEnum::len
adalah jumlah total entri dalam pencacahan itu. Hal ini berbeda dengan jumlah total nilai, yang mungkin lebih kecil bila nilai diduplikasi.
Struktur
HIDL tidak mendukung struct anonim. Jika tidak, struct di HIDL sangat mirip dengan C.
HIDL tidak mendukung struktur data dengan panjang variabel yang terkandung seluruhnya dalam sebuah struct. Ini termasuk array dengan panjang tidak terbatas yang kadang-kadang digunakan sebagai bidang terakhir dari sebuah struct di C/C++ (terkadang terlihat dengan ukuran [0]
). HIDL vec<T>
mewakili array berukuran dinamis dengan data disimpan dalam buffer terpisah; instance seperti itu direpresentasikan dengan instance vec<T>
di struct
.
Demikian pula, string
dapat dimuat dalam sebuah struct
(buffer terkait terpisah). Dalam C++ yang dihasilkan, instance tipe pegangan HIDL direpresentasikan melalui pointer ke pegangan asli sebenarnya karena instance tipe data yang mendasarinya memiliki panjang variabel.
Persatuan
HIDL tidak mendukung serikat pekerja anonim. Kalau tidak, serikat pekerja mirip dengan C.
Serikat pekerja tidak boleh berisi tipe perbaikan (pointer, deskriptor file, objek pengikat, dll.). Mereka tidak memerlukan bidang khusus atau tipe terkait dan cukup disalin melalui memcpy()
atau yang setara. Suatu serikat pekerja tidak boleh secara langsung memuat (atau memuat melalui struktur data lain) apa pun yang memerlukan pengaturan offset pengikat (yaitu, pegangan atau referensi antarmuka pengikat). Misalnya:
union UnionType { uint32_t a; // vec<uint32_t> r; // Error: can't contain a vec<T> uint8_t b;1 }; fun8(UnionType info); // Legal
Serikat pekerja juga dapat dideklarasikan di dalam struct. Misalnya:
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 }