HIDL

Bahasa definisi antarmuka HAL atau HIDL adalah bahasa deskripsi antarmuka (IDL) untuk menentukan antarmuka antara HAL dan penggunanya. HIDL memungkinkan penentuan jenis dan panggilan metode, yang dikumpulkan ke dalam antarmuka dan paket. Secara lebih luas, HIDL adalah sistem untuk berkomunikasi antar-codebase yang dapat dikompilasi secara independen.

HIDL dimaksudkan untuk digunakan untuk komunikasi antarproses (IPC). HAL yang dibuat dengan HDL disebut HAL binder karena dapat berkomunikasi dengan lapisan arsitektur lain menggunakan panggilan komunikasi antar-proses (IPC) binder. HAL yang di-binderkan berjalan dalam proses terpisah dari klien yang menggunakannya. Untuk library yang harus ditautkan ke proses, mode passthrough juga tersedia (tidak didukung di Java).

HIDL menentukan struktur data dan tanda tangan metode, yang diatur dalam antarmuka (mirip dengan class) yang dikumpulkan ke dalam paket. Sintaksis HIDL terlihat familier bagi programmer C++ dan Java, tetapi dengan kumpulan kata kunci yang berbeda. HIDL juga menggunakan anotasi gaya Java.

Terminologi

Bagian ini menggunakan istilah terkait HIDL berikut:

binderized Menunjukkan bahwa HIDL sedang digunakan untuk panggilan prosedur jarak jauh di antara proses, yang diimplementasikan melalui mekanisme seperti Binder. Lihat juga passthrough.
callback, asinkron Antarmuka yang ditayangkan oleh pengguna HAL, diteruskan ke HAL (menggunakan metode HIDL), dan dipanggil oleh HAL untuk menampilkan data kapan saja.
callback, sinkron Menampilkan data dari implementasi metode HIDL server ke klien. Tidak digunakan untuk metode yang menampilkan void atau satu nilai primitif.
klien Proses yang memanggil metode antarmuka tertentu. Proses framework Android atau HAL dapat berupa klien dari satu antarmuka dan server dari antarmuka lainnya. Lihat juga passthrough.
memperluas Menunjukkan antarmuka yang menambahkan metode dan/atau jenis ke antarmuka lain. Antarmuka hanya dapat memperluas satu antarmuka lainnya. Dapat digunakan untuk penambahan versi minor dalam nama paket yang sama atau untuk paket baru (misalnya, ekstensi vendor) untuk mem-build pada paket lama.
menghasilkan Menunjukkan metode antarmuka yang menampilkan nilai ke klien. Untuk menampilkan satu nilai non-primitif, atau lebih dari satu nilai, fungsi callback sinkron akan dibuat.
antarmuka Kumpulan metode dan jenis. Diterjemahkan menjadi class di C++ atau Java. Semua metode dalam antarmuka dipanggil dalam arah yang sama: proses klien memanggil metode yang diimplementasikan oleh proses server.
sekali jalan Jika diterapkan ke metode HIDL, menunjukkan bahwa metode tidak menampilkan nilai dan tidak memblokir.
paket Kumpulan antarmuka dan jenis data yang berbagi versi.
passthrough Mode HIDL dengan server adalah library bersama, yang di-dlopen oleh klien. Dalam mode passthrough, klien dan server adalah proses yang sama, tetapi codebase-nya terpisah. Hanya digunakan untuk memasukkan codebase lama ke dalam model HIDL. Lihat juga Binderized.
server Proses yang mengimplementasikan metode antarmuka. Lihat juga passthrough.
transportasi Infrastruktur HIDL yang memindahkan data antara server dan klien.
version Versi paket. Terdiri dari dua bilangan bulat, utama dan minor. Penambahan versi minor dapat menambahkan (tetapi tidak mengubah) jenis dan metode.

Desain HIDL

Tujuan HIDL adalah agar framework Android dapat diganti tanpa harus mem-build ulang HAL. HAL dibuat oleh vendor atau pembuat SOC dan dimasukkan ke dalam partisi /vendor di perangkat, sehingga framework Android, dalam partisinya sendiri, dapat diganti dengan OTA tanpa mengompilasi ulang HAL.

Desain HIDL menyeimbangkan masalah berikut:

  • Interoperabilitas. Buat antarmuka yang dapat dioperasikan secara andal antara proses yang dapat dikompilasi dengan berbagai arsitektur, toolchain, dan konfigurasi build. Antarmuka HIDL diberi versi dan tidak dapat diubah setelah dipublikasikan.
  • Efisiensi. HIDL mencoba meminimalkan jumlah operasi salin. Data yang ditentukan HIDL dikirim ke kode C++ dalam struktur data tata letak standar C++ yang dapat digunakan tanpa mengekstrak. HIDL juga menyediakan antarmuka memori bersama dan, karena RPC pada dasarnya agak lambat, HIDL mendukung dua cara untuk mentransfer data tanpa menggunakan panggilan RPC: memori bersama dan Antrean Pesan Cepat (FMQ).
  • Intuitif. HIDL menghindari masalah kepemilikan memori yang rumit dengan hanya menggunakan parameter in untuk RPC (lihat Android Interface Definition Language (AIDL)); nilai yang tidak dapat ditampilkan secara efisien dari metode ditampilkan melalui fungsi callback. Meneruskan data ke HIDL untuk transfer atau menerima data dari HIDL tidak akan mengubah kepemilikan data—kepemilikan selalu tetap ada pada fungsi panggilan. Data hanya perlu dipertahankan selama durasi fungsi yang dipanggil dan dapat dihancurkan segera setelah fungsi yang dipanggil ditampilkan.

Menggunakan mode passthrough

Untuk mengupdate perangkat yang menjalankan versi Android sebelumnya ke Android O, Anda dapat menggabungkan HAL konvensional (dan lama) dalam antarmuka HIDL baru yang menyalurkan HAL dalam mode binderized dan proses yang sama (passthrough). Penggabungan ini transparan untuk HAL dan framework Android.

Mode passthrough hanya tersedia untuk klien dan implementasi C++. Perangkat yang menjalankan Android versi sebelumnya tidak memiliki HAL yang ditulis dalam Java, sehingga HAL Java secara inheren di-binder.

Saat file .hal dikompilasi, hidl-gen menghasilkan file header passthrough tambahan BsFoo.h selain header yang digunakan untuk komunikasi binder; header ini menentukan fungsi yang akan dlopen. Karena HAL passthrough berjalan dalam proses yang sama dengan tempatnya dipanggil, dalam sebagian besar kasus, metode passthrough dipanggil oleh panggilan fungsi langsung (thread yang sama). Metode oneway berjalan di thread-nya sendiri karena tidak dimaksudkan untuk menunggu HAL memprosesnya (ini berarti HAL yang menggunakan metode oneway dalam mode passthrough harus aman untuk thread).

Dengan IFoo.hal, BsFoo.h menggabungkan metode yang dihasilkan HIDL untuk menyediakan fitur tambahan (seperti membuat transaksi oneway berjalan di thread lain). File ini mirip dengan BpFoo.h, tetapi alih-alih meneruskan panggilan IPC menggunakan binder, fungsi yang diinginkan langsung dipanggil. Implementasi HAL mendatang dapat menyediakan beberapa implementasi, seperti HAL FooFast dan HAL FooAccurate. Dalam kasus tersebut, file untuk setiap implementasi tambahan akan dibuat (mis., PTFooFast.cpp dan PTFooAccurate.cpp).

Melakukan binderisasi HAL passthrough

Anda dapat meng-binderkan implementasi HAL yang mendukung mode passthrough. Dengan antarmuka HAL a.b.c.d@M.N::IFoo, dua paket akan dibuat:

  • a.b.c.d@M.N::IFoo-impl. Berisi implementasi HAL dan mengekspos fungsi IFoo* HIDL_FETCH_IFoo(const char* name). Di perangkat lama, paket ini di-dlopen dan implementasinya dibuat instance-nya menggunakan HIDL_FETCH_IFoo. Anda dapat membuat kode dasar menggunakan hidl-gen dan -Lc++-impl serta -Landroidbp-impl.
  • a.b.c.d@M.N::IFoo-service. Membuka HAL passthrough dan mendaftarkan dirinya sebagai layanan binderized, sehingga implementasi HAL yang sama dapat digunakan sebagai passthrough dan binderized.

Dengan jenis IFoo, Anda dapat memanggil sp<IFoo> IFoo::getService(string name, bool getStub) untuk mendapatkan akses ke instance IFoo. Jika getStub bernilai benar, getService akan mencoba membuka HAL hanya dalam mode passthrough. Jika getStub salah, getService akan mencoba menemukan layanan binderized; jika gagal, getService akan mencoba menemukan layanan passthrough. Parameter getStub tidak boleh digunakan kecuali di defaultPassthroughServiceImplementation. (Perangkat yang diluncurkan dengan Android O adalah perangkat yang sepenuhnya di-binder, sehingga membuka layanan dalam mode passthrough tidak diizinkan.)

Tata bahasa HIDL

Secara desain, bahasa HIDL mirip dengan C (tetapi tidak menggunakan preprosesor C). Semua tanda baca yang tidak dijelaskan di bawah (selain penggunaan = dan | yang jelas) adalah bagian dari tata bahasa.

Catatan: Untuk mengetahui detail tentang gaya kode HIDL, lihat Panduan Gaya Kode.

  • /** */ menunjukkan komentar dokumentasi. Ini hanya dapat diterapkan ke deklarasi nilai enum, metode, kolom, dan jenis.
  • /* */ menunjukkan komentar multibaris.
  • // menunjukkan komentar ke akhir baris. Selain //, baris baru sama dengan spasi kosong lainnya.
  • Dalam contoh tata bahasa di bawah, teks dari // hingga akhir baris bukan bagian dari tata bahasa, tetapi merupakan komentar pada tata bahasa.
  • [empty] berarti istilah tersebut boleh kosong.
  • ? yang mengikuti literal atau istilah berarti bersifat opsional.
  • ... menunjukkan urutan yang berisi nol atau beberapa item dengan tanda baca pemisah seperti yang ditunjukkan. Tidak ada argumen variadik di HIDL.
  • Koma memisahkan elemen urutan.
  • Titik koma mengakhiri setiap elemen, termasuk elemen terakhir.
  • UPPERCASE adalah nonterminal.
  • italics adalah keluarga token seperti integer atau identifier (aturan penguraian C standar).
  • constexpr adalah ekspresi konstanta gaya C (seperti 1 + 1 dan 1L << 3).
  • import_name adalah nama paket atau antarmuka, yang memenuhi syarat seperti yang dijelaskan dalam Pembuatan Versi Hidl.
  • words huruf kecil adalah token literal.

Contoh:

ROOT =
    PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... }  // not for types.hal
  | PACKAGE IMPORTS ITEM ITEM...  // only for types.hal; no method definitions

ITEM =
    ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?;
  |  safe_union identifier { UFIELD; UFIELD; ...};
  |  struct identifier { SFIELD; SFIELD; ...};  // Note - no forward declarations
  |  union identifier { UFIELD; UFIELD; ...};
  |  enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar
  |  typedef TYPE identifier;

VERSION = integer.integer;

PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION;

PREAMBLE = interface identifier EXTENDS

EXTENDS = <empty> | extends import_name  // must be interface, not package

GENERATES = generates (FIELD, FIELD ...)

// allows the Binder interface to be used as a type
// (similar to typedef'ing the final identifier)
IMPORTS =
   [empty]
  |  IMPORTS import import_name;

TYPE =
  uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t |
 float | double | bool | string
|  identifier  // must be defined as a typedef, struct, union, enum or import
               // including those defined later in the file
|  memory
|  pointer
|  vec<TYPE>
|  bitfield<TYPE>  // TYPE is user-defined enum
|  fmq_sync<TYPE>
|  fmq_unsync<TYPE>
|  TYPE[SIZE]

FIELD =
   TYPE identifier

UFIELD =
   TYPE identifier
  |  safe_union identifier { FIELD; FIELD; ...} identifier;
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SFIELD =
   TYPE identifier
  |  safe_union identifier { FIELD; FIELD; ...};
  |  struct identifier { FIELD; FIELD; ...};
  |  union identifier { FIELD; FIELD; ...};
  |  safe_union identifier { FIELD; FIELD; ...} identifier;
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SIZE =  // Must be greater than zero
     constexpr

ANNOTATIONS =
     [empty]
  |  ANNOTATIONS ANNOTATION

ANNOTATION =
  |  @identifier
  |  @identifier(VALUE)
  |  @identifier(ANNO_ENTRY, ANNO_ENTRY  ...)

ANNO_ENTRY =
     identifier=VALUE

VALUE =
     "any text including \" and other escapes"
  |  constexpr
  |  {VALUE, VALUE ...}  // only in annotations

ENUM_ENTRY =
     identifier
  |  identifier = constexpr