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 terbinderasi (binderized HAL) sehingga dapat berkomunikasi dengan lapisan arsitektur lain menggunakan panggilan komunikasi antar-proses binder (IPC). HAL terbiner 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 memiliki kumpulan kata kunci yang berbeda. HIDL juga menggunakan anotasi gaya Java.

Terminologi

Bagian ini menggunakan istilah terkait HIDL berikut:

di-binderized Menunjukkan HIDL digunakan untuk panggilan prosedur jarak jauh antar-proses, yang diimplementasikan melalui mekanisme mirip Binder. Lihat juga passthrough.
callback, asinkron Antarmuka yang disajikan 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 nilai primitif tunggal atau kosong.
klien Proses yang memanggil metode antarmuka tertentu. Proses framework HAL atau Android dapat berupa klien dari satu antarmuka dan server Lihat juga passthrough.
memperluas Menunjukkan antarmuka yang menambahkan metode dan/atau jenis ke antarmuka lain. Sebuah 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 di-build pada paket yang lebih lama.
membuat Menunjukkan metode antarmuka yang mengembalikan 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 ke dalam class dalam C++ atau Java. Semua metode dalam antarmuka dipanggil ke arah yang sama: proses klien memanggil metode yang diterapkan oleh proses server.
sekali jalan Jika diterapkan ke metode HIDL, menunjukkan bahwa metode tidak menampilkan nilai dan tidak memblokir.
paket Kumpulan antarmuka dan tipe data yang menggunakan satu versi.
passthrough Mode HIDL dengan server yang merupakan library bersama, dlopen oleh klien. Dalam mode passthrough, klien dan server adalah proses yang sama, tetapi terpisah codebase. Digunakan hanya untuk menghadirkan codebase lama ke model HIDL. Lihat juga Terikat.
server Proses yang mengimplementasikan metode antarmuka. Lihat juga passthrough.
transportasi Infrastruktur HIDL yang memindahkan data antara server dan klien.
versi Versi paket. Terdiri dari dua bilangan bulat, besar dan kecil. Peningkatan versi kecil dapat menambahkan (tetapi tidak mengubah) jenis dan metode.

Desain HIDL

Tujuan HIDL adalah bahwa framework Android dapat diganti tanpa harus membuat ulang HAL. HAL akan di-build 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 beberapa hal berikut:

  • Interoperabilitas. Membuat antarmuka dengan interoperabilitas yang andal antarproses, yang dapat dikompilasi dengan berbagai arsitektur, toolchain, dan konfigurasi build. Antarmuka HIDL memiliki beberapa versi dan tidak dapat diubah setelah dipublikasikan.
  • Efisiensi. HIDL mencoba meminimalkan jumlah operasi penyalinan. Data yang ditentukan HIDL dikirim ke kode C++ dalam struktur data tata letak standar C++ yang dapat digunakan tanpa membuka paket. HIDL juga menyediakan antarmuka memori bersama. Karena RPC pada dasarnya agak lambat, HIDL mendukung dua cara untuk mentransfer data tanpa menggunakan panggilan RPC: memori bersama dan Fast Message Queue (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 akan ditampilkan melalui fungsi callback. Meneruskan data ke HIDL untuk transfer maupun menerima data dari HIDL tidak akan mengubah kepemilikan data—kepemilikan akan selalu tetap dengan fungsi panggilan. Data hanya perlu dipertahankan selama durasi fungsi yang dipanggil dan dapat segera dihancurkan setelah fungsi yang dipanggil ditampilkan.

Menggunakan mode passthrough

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

Mode passthrough hanya tersedia untuk klien dan implementasi C++. Perangkat yang menjalankan versi Android yang lebih lama tidak memiliki HAL yang ditulis dalam Java, sehingga HAL Java secara inheren dibinder.

Saat file .hal dikompilasi, hidl-gen akan menghasilkan file header passthrough tambahan BsFoo.h selain header yang digunakan untuk komunikasi binder; header ini menentukan fungsi yang akan dlopendikompilasi. Saat HAL passthrough berjalan dalam proses yang sama dengan pemanggilan, 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 apa pun yang menggunakan metode oneway dalam mode passthrough harus aman untuk thread).

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

Membuat HAL passthrough

Anda bisa mem-binder implementasi HAL yang mendukung mode passthrough. Dengan antarmuka HAL a.b.c.d@M.N::IFoo, dua paket 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 dibuat dlopen dan implementasinya akan dibuat instance-nya menggunakan HIDL_FETCH_IFoo. Anda dapat membuat kode dasar menggunakan hidl-gen, -Lc++-impl, dan -Landroidbp-impl.
  • a.b.c.d@M.N::IFoo-service. Membuka HAL passthrough dan mendaftarkan diri sebagai layanan pengikat, yang memungkinkan implementasi HAL yang sama untuk digunakan sebagai passthrough dan binder.

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 berupaya menemukan layanan binderized; jika gagal, layanan akan mencoba menemukan layanan passthrough. Parameter getStub tidak boleh digunakan kecuali dalam defaultPassthroughServiceImplementation. (Perangkat yang diluncurkan dengan Android O adalah perangkat yang diikat sepenuhnya, sehingga tidak diizinkan membuka layanan dalam mode passthrough.)

Tata bahasa HIDL

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

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

  • /** */ menunjukkan komentar dokumentasi. Ini hanya dapat diterapkan pada deklarasi nilai jenis, metode, kolom, dan enum.
  • /* */ menunjukkan komentar multibaris.
  • // menunjukkan komentar hingga 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, melainkan komentar pada tata bahasa.
  • [empty] berarti istilah boleh kosong.
  • ? yang mengikuti literal atau istilah berarti opsional.
  • ... menunjukkan urutan yang berisi nol item atau beberapa item dengan memisahkan tanda baca seperti yang ditunjukkan. Tidak ada argumen variadik dalam HIDL.
  • Koma memisahkan elemen urut.
  • Titik koma menghentikan setiap elemen, termasuk elemen terakhir.
  • HURUF BESAR adalah nonterminal.
  • italics adalah kelompok 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.
  • Huruf kecil words 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