Tipe Data

Bagian ini menjelaskan tipe data HIDL. Untuk detail implementasi, lihat HIDL C++ (untuk implementasi C++) atau HIDL Java (untuk implementasi Java).

Kesamaan dengan C++ meliputi:

  • structs menggunakan sintaks C++; unions mendukung sintaks C++ secara default. Keduanya harus disebutkan; struct dan union anonim tidak didukung.
  • Typedefs diperbolehkan dalam HIDL (seperti dalam C++).
  • Komentar gaya C++ diperbolehkan dan disalin ke file header yang dihasilkan.

Kesamaan dengan Java meliputi:

  • 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 gaya 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 Standard-Layout (bagian dari persyaratan tipe data lama biasa) memiliki tata letak memori yang konsisten dalam kode C++ yang dihasilkan, ditegakkan dengan atribut penyelarasan eksplisit pada struct dan anggota union .

Tipe HIDL primitif, serta tipe enum dan bitfield (yang selalu diturunkan dari tipe primitif), dipetakan ke tipe C++ standar seperti std::uint32_t uint32_t dari cstdint .

Karena Java tidak mendukung tipe unsigned, tipe HIDL unsigned dipetakan ke tipe Java bertanda yang sesuai. Struct memetakan 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 diterjemahkan, dan mungkin tidak identik pada terjemahan ulang karena set karakter tidak selalu dipetakan dengan mulus.

Data yang diterima melalui IPC di C++ ditandai const dan berada dalam memori hanya-baca yang hanya bertahan selama panggilan 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 diuraikan oleh backend Vendor Test Suite (VTS) dari kompiler HIDL tetapi tidak ada anotasi yang diuraikan tersebut yang benar-benar dipahami oleh kompiler HIDL. Sebagai gantinya, anotasi VTS yang diurai ditangani oleh VTS Compiler (VTSC).

Anotasi menggunakan sintaks Java: @annotation atau @annotation(value) atau @annotation(id=value, id=value…) di mana nilai dapat berupa ekspresi konstan, string, atau daftar nilai di dalam {} , seperti pada Jawa. Beberapa anotasi dengan nama yang sama dapat dilampirkan ke item yang sama.

Deklarasi maju

Dalam HIDL, struct mungkin tidak dideklarasikan ke depan, membuat tipe data referensi mandiri yang ditentukan pengguna menjadi tidak mungkin (misalnya, Anda tidak dapat mendeskripsikan daftar tertaut atau pohon dalam HIDL). Sebagian besar HAL yang ada (sebelum Android 8.x) memiliki penggunaan deklarasi penerusan yang terbatas, yang dapat dihapus 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 referensi sendiri. Jika data yang sama dilewatkan dua kali, seperti dengan dua parameter metode atau vec<T> s yang menunjuk ke data yang sama, dua salinan terpisah dibuat dan dikirimkan.

Deklarasi bersarang

HIDL mendukung deklarasi bersarang ke level sebanyak yang diinginkan (dengan satu pengecualian yang disebutkan di bawah). Sebagai contoh:

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
    }
    …

Pengecualian adalah bahwa jenis antarmuka hanya dapat disematkan di vec<T> dan hanya satu level dalam (tidak ada vec<vec<IFoo>> ).

Sintaks penunjuk mentah

Bahasa HIDL tidak menggunakan * dan tidak mendukung fleksibilitas penuh pointer mentah C/C++. Untuk detail tentang bagaimana 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 dilihat sebagai antarmuka umum dan sinonim untuk 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. Itu 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 nested vec, struct, array, atau union.

MQDescriptorSync & MQDescriptorUnsync

Jenis MQDescriptorSync dan MQDescriptorUnsync meneruskan deskriptor Antrian Pesan Cepat (FMQ) yang disinkronkan atau tidak disinkronkan di seluruh antarmuka HIDL. Untuk detailnya, lihat HIDL C++ (FMQ tidak didukung di Java).

tipe memori

Jenis memory digunakan untuk mewakili memori bersama yang tidak dipetakan dalam HIDL. Ini hanya didukung di C++. Nilai jenis ini dapat digunakan di sisi 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 berupa tipe yang formatnya tidak akan pernah berubah selama versi antarmuka yang melewati memory . Jika tidak, HAL dapat mengalami masalah kompatibilitas yang fatal.

tipe penunjuk

Jenis pointer hanya untuk penggunaan internal HIDL.

templat tipe bitfield<T>

bitfield<T> di mana T adalah enum yang ditentukan pengguna menunjukkan nilainya adalah bitwise-ATAU 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 jenis 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 bitwise-OR dari Flag (yaitu mengetahui bahwa memanggil setFlags dengan 16 tidak valid). Tanpa bitfield , informasi ini hanya disampaikan melalui dokumentasi. Selain itu, VTS benar-benar dapat memeriksa apakah nilai flag adalah bitwise-ATAU dari Flag.

menangani tipe primitif

PERINGATAN: Alamat apa pun (bahkan alamat perangkat fisik) tidak boleh menjadi bagian dari pegangan asli. Melewati informasi ini antar proses berbahaya dan membuat mereka rentan terhadap serangan. Setiap nilai yang dilewatkan di antara proses harus divalidasi sebelum digunakan untuk mencari memori yang dialokasikan dalam suatu proses. Jika tidak, pegangan yang buruk dapat menyebabkan akses memori yang buruk atau kerusakan memori.

Semantik HIDL adalah copy-by-value, yang menyiratkan bahwa parameter disalin. Setiap potongan besar data, atau data yang perlu dibagi antar proses (seperti pagar sinkronisasi), ditangani dengan menyebarkan deskriptor file yang menunjuk ke objek persisten: ashmem untuk memori bersama, file aktual, atau apa pun yang dapat bersembunyi di balik deskriptor file. Pengandar pengikat menduplikasi deskriptor file ke dalam proses lain.

native_handle_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 diedarkan berdasarkan nilai. Satu deskriptor file dapat disimpan dalam pegangan asli tanpa int dan satu deskriptor file. Melewati pegangan menggunakan pegangan asli yang dienkapsulasi dengan tipe primitif handle memastikan bahwa pegangan asli disertakan secara langsung dalam HIDL.

Karena native_handle_t memiliki ukuran variabel, ia tidak dapat dimasukkan langsung ke dalam struct. Bidang pegangan menghasilkan penunjuk ke native_handle_t yang dialokasikan secara terpisah.

Di versi Android sebelumnya, pegangan asli dibuat menggunakan fungsi yang sama yang ada di libcutils . Di Android 8.0 dan lebih tinggi, fungsi ini sekarang disalin ke android::hardware::hidl namespace atau dipindahkan ke NDK. Kode autogenerated HIDL membuat serialisasi dan deserialize fungsi-fungsi ini secara otomatis, tanpa keterlibatan dari kode yang ditulis pengguna.

Menangani dan mengajukan kepemilikan deskriptor

Saat Anda memanggil metode antarmuka HIDL yang meneruskan (atau mengembalikan) objek hidl_handle (tingkat atas atau bagian dari tipe gabungan), kepemilikan deskriptor file yang terkandung di dalamnya adalah sebagai berikut:

  • Pemanggil yang meneruskan objek hidl_handle sebagai argumen mempertahankan kepemilikan deskriptor file yang terkandung dalam native_handle_t yang dibungkusnya; penelepon harus menutup deskriptor file ini setelah selesai dengan mereka.
  • Proses mengembalikan objek hidl_handle (dengan meneruskannya ke fungsi _cb ) mempertahankan kepemilikan deskriptor file yang terkandung dalam native_handle_t yang dibungkus oleh objek; proses harus menutup deskriptor file ini setelah selesai dengan mereka.
  • Sebuah transport yang menerima hidl_handle memiliki kepemilikan dari deskriptor file di dalam native_handle_t yang dibungkus oleh objek; penerima dapat menggunakan deskriptor file ini apa adanya selama panggilan balik transaksi, tetapi harus mengkloning pegangan asli untuk menggunakan deskriptor file di luar panggilan balik. Transport 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 berisi struct:

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

string

String muncul secara berbeda di C++ dan Java, tetapi tipe penyimpanan transport yang mendasarinya adalah struktur C++. Untuk detailnya, lihat HIDL C++ Data Types atau HIDL Java Data Types .

Catatan: Melewati string ke atau dari Java melalui antarmuka HIDL (termasuk Java ke Java) akan menyebabkan konversi kumpulan karakter yang mungkin tidak benar-benar mempertahankan pengkodean asli.

vec<T> jenis templat

Template vec<T> mewakili buffer berukuran variabel yang berisi instance T .

T dapat menjadi 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

Jenis 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 , …  }

Sebuah 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 default ke nilai sebelumnya ditambah satu. Sebagai contoh:

// 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 ), defaultnya adalah nilai enumerator terakhir dari enum induk ditambah satu. Sebagai contoh:

// 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 induknya. Namun, nilai enum induk dapat digunakan dengan aman sebagai nilai enum anak karena nilai enum anak menurut definisi adalah superset dari nilai enum induk. Ingatlah hal ini saat mendesain antarmuka karena ini berarti tipe yang merujuk ke enum induk tidak dapat merujuk ke enum anak di iterasi selanjutnya dari antarmuka Anda.

Nilai enum dirujuk dengan sintaks titik dua (bukan sintaks titik sebagai tipe bersarang). Sintaksnya adalah Type:VALUE_NAME . Tidak perlu menentukan tipe jika nilai direferensikan dalam tipe enum atau tipe turunan 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, enums memiliki atribut len yang dapat digunakan dalam ekspresi konstan. MyEnum::len adalah jumlah total entri dalam enumerasi itu. Ini berbeda dari jumlah total nilai, yang mungkin lebih kecil ketika nilai digandakan.

Struktur

HIDL tidak mendukung struct anonim. Jika tidak, struct dalam HIDL sangat mirip dengan C.

HIDL tidak mendukung struktur data panjang-variabel yang seluruhnya terkandung dalam sebuah struct. Ini termasuk array indefinite-length yang terkadang digunakan sebagai bidang terakhir dari sebuah struct di C/C++ (terkadang terlihat dengan ukuran [0] ). HIDL vec<T> mewakili array berukuran dinamis dengan data yang disimpan dalam buffer terpisah; contoh seperti itu diwakili dengan contoh vec<T> di struct .

Demikian pula, string dapat dimuat dalam struct (buffer terkait terpisah). Dalam C++ yang dihasilkan, instance dari tipe pegangan HIDL diwakili melalui pointer ke pegangan asli yang sebenarnya karena instance dari tipe data yang mendasarinya memiliki panjang variabel.

Persatuan

HIDL tidak mendukung serikat anonim. Jika tidak, serikat pekerja mirip dengan C.

Serikat pekerja tidak boleh berisi jenis perbaikan (pointer, deskriptor file, objek pengikat, dll.). Mereka tidak memerlukan bidang khusus atau jenis terkait dan hanya disalin melalui memcpy() atau yang setara. Union tidak boleh secara langsung berisi (atau berisi melalui struktur data lain) apa pun yang memerlukan pengaturan offset pengikat (yaitu, pegangan atau referensi antarmuka pengikat). Sebagai contoh:

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 juga dapat dideklarasikan di dalam struct. Sebagai contoh:

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
  }