Jenis data

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

Kesamaan dengan C++ meliputi:

  • structs menggunakan sintaksis C++; unions mendukung sintaksis C++ secara default. Keduanya harus diberi nama; struct dan union anonim tidak didukung.
  • Typedef diizinkan di HIDL (seperti di C++).
  • Komentar bergaya C++ diizinkan dan disalin ke file header yang dihasilkan.

Kesamaan dengan Java meliputi:

  • Untuk setiap file, HIDL menentukan namespace bergaya Java yang harus dimulai dengan android.hardware.. Namespace C++ yang dihasilkan adalah ::android::hardware::….
  • Semua definisi file terdapat dalam wrapper 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

struct atau union yang terdiri dari Standard-Layout (subkumpulan persyaratan jenis data biasa) memiliki tata letak memori yang konsisten dalam kode C++ yang dihasilkan, yang diterapkan dengan atribut perataan eksplisit pada anggota struct dan union.

Jenis HIDL primitif, serta jenis enum dan bitfield (yang selalu berasal dari jenis primitif), dipetakan ke jenis C++ standar seperti std::uint32_t dari cstdint.

Karena Java tidak mendukung jenis yang tidak ditandatangani, jenis HIDL yang tidak ditandatangani dipetakan ke jenis Java bertanda tangan yang sesuai. Struct dipetakan ke class Java; array dipetakan ke array Java; union saat ini tidak didukung di Java. 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 saat diterjemahkan ulang karena kumpulan karakter tidak selalu dipetakan dengan lancar.

Data yang diterima melalui IPC di C++ ditandai const dan berada di memori hanya baca yang hanya ada selama durasi panggilan fungsi. Data yang diterima melalui IPC di Java telah disalin ke dalam objek Java, sehingga dapat dipertahankan tanpa penyalinan tambahan (dan dapat diubah).

Anotasi

Anotasi bergaya Java dapat ditambahkan ke deklarasi jenis. Anotasi diurai oleh backend Vendor Test Suite (VTS) dari compiler HIDL, tetapi tidak ada anotasi yang diuraikan tersebut yang benar-benar dipahami oleh compiler HIDL. Sebagai gantinya, anotasi VTS yang diuraikan ditangani oleh VTS Compiler (VTSC).

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

Meneruskan deklarasi

Di HIDL, struct mungkin tidak dideklarasikan ke depan, sehingga jenis data yang ditentukan pengguna dan merujuk pada dirinya sendiri tidak mungkin (misalnya, Anda tidak dapat mendeskripsikan linked list atau hierarki di HIDL). Sebagian besar HAL yang ada (sebelum Android 8.x) memiliki penggunaan deklarasi forward yang terbatas, yang dapat dihapus dengan mengatur ulang deklarasi struktur data.

Batasan ini memungkinkan struktur data disalin menurut nilai dengan deep-copy sederhana, bukan melacak nilai pointer yang mungkin terjadi beberapa kali dalam struktur data yang merujuk pada dirinya sendiri. Jika data yang sama diteruskan dua kali, seperti dengan dua parameter metode atau vec<T> yang mengarah ke data yang sama, dua salinan terpisah akan dibuat dan dikirim.

Deklarasi bertingkat

HIDL mendukung deklarasi bertingkat ke sebanyak level yang diinginkan (dengan satu pengecualian yang tercantum di bawah). 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 jenis antarmuka hanya dapat disematkan dalam vec<T> dan hanya satu tingkat (tidak ada vec<vec<IFoo>>).

Sintaksis pointer mentah

Bahasa HIDL tidak menggunakan * dan tidak mendukung fleksibilitas penuh pointer mentah C/C++. Untuk mengetahui detail tentang cara HIDL mengenkapsulasi pointer dan array/vektor, lihat template vec<T>.

Antarmuka

Kata kunci interface memiliki dua penggunaan.

  • Tindakan ini akan membuka definisi antarmuka dalam file .hal.
  • Jenis ini dapat digunakan sebagai jenis khusus di kolom struct/union, parameter metode, dan hasil. Fungsi 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 berjanji 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>. Elemen ini tidak boleh menjadi anggota vektor, struct, array, atau union bertingkat.

MQDescriptorSync dan MQDescriptorUnsync

Jenis MQDescriptorSync dan MQDescriptorUnsync meneruskan deskripsi Fast Message Queue (FMQ) yang disinkronkan atau tidak disinkronkan melalui antarmuka HIDL. Untuk mengetahui detailnya, lihat HIDL C++ (FMQ tidak didukung di Java).

jenis memori

Jenis memory digunakan untuk mewakili memori bersama yang tidak dipetakan di HIDL. Jenis ini hanya didukung di C++. Nilai jenis ini dapat digunakan di ujung penerima untuk menginisialisasi objek IMemory, memetakan memori, dan membuatnya dapat digunakan. Untuk mengetahui detailnya, lihat HIDL C++.

Peringatan: Data terstruktur yang ditempatkan dalam memori bersama HARUS berupa jenis yang formatnya tidak pernah berubah selama masa aktif versi antarmuka yang meneruskan memory. Jika tidak, HAL dapat mengalami masalah kompatibilitas fatal.

jenis pointer

Jenis pointer hanya untuk penggunaan internal HIDL.

Template jenis bitfield<T>

bitfield<T> dengan T adalah enum yang ditentukan pengguna menunjukkan bahwa nilainya adalah bitwise-OR dari nilai enum yang ditentukan di T. Dalam kode yang dihasilkan, bitfield<T> muncul sebagai jenis dasar T. Contoh:

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);

Compiler menangani Flag jenis yang sama dengan 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 kini mengetahui bahwa setFlags mengambil nilai Flag bitwise-OR (yaitu mengetahui bahwa memanggil setFlags dengan 16 tidak valid). Tanpa bitfield, informasi ini hanya disampaikan melalui dokumentasi. Selain itu, VTS sebenarnya dapat memeriksa apakah nilai flag adalah bitwise-OR dari Flag.

Nama sebutan jenis primitif

PERINGATAN: Alamat apa pun (bahkan alamat perangkat fisik) tidak boleh menjadi bagian dari nama sebutan native. Meneruskan informasi ini antarproses berbahaya dan membuatnya rentan terhadap serangan. Setiap nilai yang diteruskan antarproses harus divalidasi sebelum digunakan untuk mencari memori yang dialokasikan dalam proses. Jika tidak, handle yang buruk dapat menyebabkan akses memori yang buruk atau kerusakan memori.

Semantik HIDL adalah salin-menurut-nilai, yang menyiratkan bahwa parameter disalin. Setiap bagian data yang besar, atau data yang perlu dibagikan di antara proses (seperti pagar sinkronisasi), ditangani dengan meneruskan deskripsi file yang mengarah ke objek persisten: ashmem untuk memori bersama, file sebenarnya, atau hal lain yang dapat disembunyikan di balik deskripsi file. Driver binder menduplikasi deskriptor file ke dalam proses lain.

native_handle_t

Android mendukung native_handle_t, konsep nama sebutan umum yang ditentukan di 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;

Handle native adalah kumpulan int dan deskripsi file yang diteruskan berdasarkan nilai. Satu deskripsi file dapat disimpan dalam handle native tanpa int dan satu deskripsi file. Meneruskan handle menggunakan handle native yang dienkapsulasi dengan jenis primitif handle memastikan bahwa handle native disertakan langsung dalam HIDL.

Karena native_handle_t memiliki ukuran variabel, native_handle_t tidak dapat disertakan secara langsung dalam struct. Kolom handle menghasilkan pointer ke native_handle_t yang dialokasikan secara terpisah.

Pada versi Android sebelumnya, handle native dibuat menggunakan fungsi yang sama yang ada di libcutils. Di Android 8.0 dan yang lebih tinggi, fungsi ini kini disalin ke namespace android::hardware::hidl atau dipindahkan ke NDK. Kode yang dihasilkan secara otomatis oleh HIDL akan melakukan serialisasi dan deserialisasi fungsi ini secara otomatis, tanpa melibatkan kode yang ditulis pengguna.

Kepemilikan deskriptor file dan nama sebutan file

Saat Anda memanggil metode antarmuka HIDL yang meneruskan (atau menampilkan) objek hidl_handle (tingkat atas atau bagian dari jenis gabungan), kepemilikan deskripsi file yang ada di dalamnya adalah sebagai berikut:

  • Pemanggil yang meneruskan objek hidl_handle sebagai argumen mempertahankan kepemilikan deskriptor file yang terdapat dalam native_handle_t yang digabungkan; pemanggil harus menutup deskriptor file ini setelah selesai menggunakannya.
  • Proses yang menampilkan objek hidl_handle (dengan meneruskannya ke fungsi _cb) mempertahankan kepemilikan deskriptor file yang terdapat dalam native_handle_t yang digabungkan oleh objek; proses harus menutup deskriptor file ini saat selesai menggunakannya.
  • Transpor yang menerima hidl_handle memiliki kepemilikan deskripsi file di dalam native_handle_t yang digabungkan oleh objek; penerima dapat menggunakan deskripsi file ini apa adanya selama callback transaksi, tetapi harus meng-clone handle native untuk menggunakan deskripsi file di luar callback. Transpor akan otomatis memanggil close() untuk deskripsi file saat transaksi selesai.

HIDL tidak mendukung handle di Java (karena Java tidak mendukung handle 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 jenis penyimpanan transpor yang mendasarinya adalah struktur C++. Untuk mengetahui detailnya, lihat Jenis Data HIDL C++ atau Jenis Data HIDL Java.

Catatan: Meneruskan string ke atau dari Java melalui antarmuka HIDL (termasuk Java ke Java) menyebabkan konversi kumpulan karakter yang mungkin tidak mempertahankan encoding asli.

Template jenis vec<T>

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

T dapat berupa salah satu dari hal berikut:

  • Jenis 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)
  • Nama sebutan channel
  • bitfield<U>
  • vec<U>, dengan U ada dalam daftar ini kecuali antarmuka (misalnya, vec<vec<IFoo>> tidak didukung)
  • U[] (array berukuran U), dengan U ada dalam daftar ini kecuali antarmuka

Jenis yang ditentukan pengguna

Bagian ini menjelaskan jenis 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 ditentukan dalam hal salah satu jenis bilangan bulat di HIDL. Jika tidak ada nilai yang ditentukan untuk enumerator pertama enum berdasarkan jenis bilangan bulat, nilai defaultnya adalah 0. Jika tidak ada nilai yang ditentukan untuk penghitung berikutnya, nilai defaultnya adalah nilai sebelumnya ditambah satu. 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 enum turunan (dalam hal ini FullSpectrumColor), nilai defaultnya adalah nilai enumerator terakhir dari enum induk ditambah satu. Contoh:

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

Peringatan: Pewarisan enum berfungsi mundur dari sebagian besar jenis pewarisan lainnya. Nilai enum turunan tidak dapat digunakan sebagai nilai enum induk. Hal ini karena enum turunan menyertakan lebih banyak nilai daripada induk. Namun, nilai enum induk dapat digunakan dengan aman sebagai nilai enum turunan karena nilai enum turunan menurut definisinya adalah superset dari nilai enum induk. Perhatikan hal ini saat mendesain antarmuka karena hal ini berarti jenis yang merujuk ke enum induk tidak dapat merujuk ke enum turunan dalam iterasi antarmuka Anda berikutnya.

Nilai enum dirujuk dengan sintaksis titik dua (bukan sintaksis titik sebagai jenis bertingkat). Sintaksisnya adalah Type:VALUE_NAME. Tidak perlu menentukan jenis jika nilai direferensikan dalam jenis enum atau jenis 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, enum memiliki atribut len yang dapat digunakan dalam ekspresi konstan. MyEnum::len adalah jumlah total entri dalam enumerasi tersebut. Hal ini berbeda dengan jumlah total nilai, yang mungkin lebih kecil jika nilai diduplikasi.

Struct

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

HIDL tidak mendukung struktur data panjang variabel yang sepenuhnya terdapat dalam struct. Hal ini mencakup array dengan panjang tidak terbatas yang terkadang digunakan sebagai kolom terakhir struct di C/C++ (terkadang terlihat dengan ukuran [0]). HIDL vec<T> mewakili array dengan ukuran dinamis dengan data yang disimpan dalam buffer terpisah; instance tersebut direpresentasikan dengan instance vec<T> di struct.

Demikian pula, string dapat dimuat dalam struct (buffer terkait terpisah). Dalam C++ yang dihasilkan, instance jenis handle HIDL direpresentasikan melalui pointer ke handle native yang sebenarnya karena instance jenis data pokok memiliki panjang variabel.

Union

HIDL tidak mendukung union anonim. Jika tidak, union mirip dengan C.

Union tidak boleh berisi jenis perbaikan (seperti pointer, deskriptor file, objek binder). Jenis ini tidak memerlukan kolom khusus atau jenis terkait dan hanya disalin menggunakan memcpy() atau yang setara. Union mungkin tidak langsung berisi (atau berisi menggunakan struktur data lain) apa pun yang memerlukan penetapan offset binder (yaitu, handle atau referensi antarmuka binder). 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. 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
  }