Jenis Data

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 dalam native_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 dalam native_handle_t yang dibungkus oleh objek; prosesnya harus menutup deskriptor file ini setelah selesai.
  • Transportasi yang menerima hidl_handle memiliki kepemilikan deskriptor file di dalam native_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 otomatis close() 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
  }