Android 11 memperkenalkan kemampuan untuk menggunakan AIDL untuk HAL di Android, sehingga memungkinkan penerapan bagian Android tanpa HIDL. Transisikan HAL untuk menggunakan AIDL secara eksklusif jika memungkinkan (jika HAL upstream menggunakan HIDL, HIDL harus digunakan).
HAL yang menggunakan AIDL untuk berkomunikasi antara komponen framework, seperti yang ada di
system.img, dan komponen hardware, seperti yang ada di vendor.img, harus menggunakan
AIDL stabil. Namun, untuk berkomunikasi dalam partisi, misalnya, dari satu
HAL ke HAL lain, tidak ada batasan pada mekanisme IPC yang digunakan.
Motivasi
AIDL sudah ada lebih lama daripada HIDL, dan digunakan di banyak tempat lain, seperti di antara komponen framework Android atau di aplikasi. Setelah AIDL memiliki dukungan stabilitas, Anda dapat menerapkan seluruh stack dengan satu runtime IPC. AIDL juga memiliki sistem pembuatan versi yang lebih baik daripada HIDL. Berikut beberapa keunggulan AIDL:
- Menggunakan satu bahasa IPC berarti hanya ada satu hal yang perlu dipelajari, di-debug, dioptimalkan, dan diamankan.
- AIDL mendukung pembuatan versi di tempat untuk pemilik antarmuka:
- Pemilik dapat menambahkan metode ke akhir antarmuka, atau kolom ke parcelable. Artinya, kode versi lebih mudah dikelola dari tahun ke tahun, dan biaya dari tahun ke tahun juga lebih kecil (jenis dapat diubah di tempat dan tidak perlu library tambahan untuk setiap versi antarmuka).
- Antarmuka ekstensi dapat dilampirkan pada waktu proses, bukan di sistem jenis, sehingga tidak perlu merebase ekstensi hilir ke versi antarmuka yang lebih baru.
- Antarmuka AIDL yang ada dapat digunakan secara langsung saat pemiliknya memilih untuk menstabilkannya. Sebelumnya, seluruh salinan antarmuka harus dibuat di HIDL.
Mem-build terhadap runtime AIDL
AIDL memiliki tiga backend yang berbeda: Java, NDK, dan CPP. Untuk menggunakan AIDL stabil,
selalu gunakan salinan sistem libbinder di system/lib*/libbinder.so dan
berbicara di /dev/binder. Untuk kode pada image vendor, ini berarti libbinder (dari VNDK) tidak dapat digunakan: library ini memiliki API C++ yang tidak stabil dan internal yang tidak stabil. Sebagai gantinya, kode vendor native harus menggunakan backend AIDL NDK, ditautkan ke libbinder_ndk (yang didukung oleh libbinder.so sistem), dan ditautkan ke library NDK yang dibuat oleh entri aidl_interface. Untuk mengetahui nama modul yang tepat, lihat Aturan penamaan modul.
Menulis antarmuka HAL AIDL
Agar antarmuka AIDL dapat digunakan antara sistem dan vendor, antarmuka memerlukan dua perubahan:
- Setiap definisi jenis harus diberi anotasi dengan
@VintfStability. - Pernyataan
aidl_interfaceharus menyertakanstability: "vintf",.
Hanya pemilik antarmuka yang dapat melakukan perubahan ini.
Saat Anda melakukan perubahan ini, antarmuka harus ada di
manifes VINTF agar berfungsi. Uji ini (dan persyaratan terkait, seperti memverifikasi bahwa antarmuka yang dirilis sudah difinalisasi) menggunakan pengujian Vendor Test Suite (VTS) vts_treble_vintf_vendor_test. Anda dapat menggunakan antarmuka
@VintfStability tanpa persyaratan ini dengan memanggil
AIBinder_forceDowngradeToLocalStability di backend NDK,
android::Stability::forceDowngradeToLocalStability di backend C++,
atau android.os.Binder#forceDowngradeToSystemStability di backend Java
pada objek binder sebelum dikirim ke proses lain.
Selain itu, untuk portabilitas kode maksimum dan menghindari potensi masalah seperti library tambahan yang tidak perlu, nonaktifkan backend CPP.
Kode ini menunjukkan cara menonaktifkan backend CPP:
aidl_interface: {
...
backend: {
cpp: {
enabled: false,
},
},
}
Menemukan antarmuka HAL AIDL
Antarmuka AIDL stabil AOSP untuk HAL berada dalam folder aidl di direktori dasar yang sama dengan antarmuka HIDL:
hardware/interfacesadalah untuk antarmuka yang biasanya disediakan oleh hardware.frameworks/hardware/interfacesadalah untuk antarmuka tingkat tinggi yang disediakan untuk hardware.system/hardware/interfacesadalah untuk antarmuka tingkat rendah yang disediakan untuk hardware.
Masukkan antarmuka ekstensi ke subdirektori hardware/interfaces
lain di vendor atau hardware.
Antarmuka ekstensi
Android memiliki serangkaian antarmuka AOSP resmi dengan setiap rilis. Jika partner Android ingin menambahkan kemampuan ke antarmuka ini, mereka tidak boleh mengubahnya secara langsung karena hal ini akan membuat runtime Android mereka tidak kompatibel dengan runtime Android AOSP. Hindari mengubah antarmuka ini agar image GSI dapat terus berfungsi.
Ekstensi dapat didaftarkan dengan dua cara berbeda:
- Saat runtime; lihat Antarmuka ekstensi terlampir
- Sebagai mandiri, terdaftar secara global dan di VINTF
Namun, terlepas dari cara pendaftaran ekstensi, jika komponen khusus vendor (yang berarti bukan bagian dari AOSP upstream) menggunakan antarmuka, konflik penggabungan tidak mungkin terjadi. Namun, jika modifikasi downstream pada komponen AOSP upstream dilakukan, konflik penggabungan dapat terjadi, dan strategi berikut direkomendasikan:
- Lakukan pengiriman tambahan antarmuka ke AOSP dalam rilis berikutnya.
- Penambahan antarmuka upstream yang memungkinkan fleksibilitas lebih lanjut (tanpa konflik penggabungan) dalam rilis berikutnya.
Parcelable ekstensi: ParcelableHolder
ParcelableHolder adalah instance antarmuka Parcelable yang dapat berisi instance Parcelable lainnya.
Kasus penggunaan utama ParcelableHolder adalah untuk membuat Parcelable dapat diperluas.
Misalnya, image yang diharapkan oleh para pengimplementasi perangkat dapat memperluas Parcelable, AospDefinedParcelable yang ditentukan AOSP, untuk menyertakan fitur bernilai tambah mereka.
Gunakan antarmuka ParcelableHolder untuk memperluas Parcelable dengan fitur bernilai tambah Anda. Antarmuka ParcelableHolder berisi instance
Parcelable. Jika Anda mencoba menambahkan kolom langsung ke Parcelable, error akan terjadi:
parcelable AospDefinedParcelable {
int a;
String b;
String x; // ERROR: added by a device implementer
int[] y; // added by a device implementer
}
Seperti yang terlihat dalam kode sebelumnya, praktik ini tidak berfungsi karena kolom
yang ditambahkan oleh pengimplementasi perangkat mungkin mengalami konflik saat Parcelable
direvisi dalam rilis Android berikutnya.
Dengan menggunakan ParcelableHolder, pemilik parcelable dapat menentukan titik ekstensi dalam instance Parcelable:
parcelable AospDefinedParcelable {
int a;
String b;
ParcelableHolder extension;
}
Kemudian, pengimplementasi perangkat dapat menentukan instance Parcelable mereka sendiri untuk
ekstensinya:
parcelable OemDefinedParcelable {
String x;
int[] y;
}
Instance Parcelable baru dapat dilampirkan ke Parcelable asli dengan kolom ParcelableHolder:
// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;
ap.extension.setParcelable(op);
...
OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);
// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();
ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);
...
std::shared_ptr<OemDefinedParcelable> op_ptr;
ap.extension.getParcelable(&op_ptr);
// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);
...
std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);
// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });
ap.extension.set_parcelable(Rc::clone(&op));
...
let op = ap.extension.get_parcelable::<OemDefinedParcelable>();
Nama instance server HAL AIDL
Menurut konvensi, layanan AIDL HAL memiliki nama instance dengan format
$package.$type/$instance. Misalnya, instance HAL vibrator didaftarkan sebagai android.hardware.vibrator.IVibrator/default.
Menulis server HAL AIDL
@VintfStability Server AIDL harus dideklarasikan dalam manifes VINTF, misalnya:
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>1</version>
<fqname>IVibrator/default</fqname>
</hal>
Jika tidak, mereka harus mendaftarkan layanan AIDL secara normal. Saat menjalankan pengujian VTS, semua HAL AIDL yang dideklarasikan diharapkan tersedia.
Menulis klien AIDL
Klien AIDL harus mendeklarasikan diri mereka dalam matriks kompatibilitas, misalnya:
<hal format="aidl" optional="true">
<name>android.hardware.vibrator</name>
<version>1-2</version>
<interface>
<name>IVibrator</name>
<instance>default</instance>
</interface>
</hal>
Mengonversi HAL yang ada dari HIDL ke AIDL
Gunakan alat hidl2aidl untuk mengonversi antarmuka HIDL ke AIDL.
Fitur hidl2aidl:
- Buat file AIDL (
.aidl) berdasarkan file HAL (.hal) untuk paket tertentu. - Buat aturan build untuk paket AIDL yang baru dibuat dengan semua backend diaktifkan.
- Buat metode terjemahan di backend Java, CPP, dan NDK untuk menerjemahkan dari jenis HIDL ke jenis AIDL.
- Buat aturan build untuk library terjemahan dengan dependensi yang diperlukan.
- Buat pernyataan statis untuk memastikan enumerator HIDL dan AIDL memiliki nilai yang sama di backend CPP dan NDK.
Ikuti langkah-langkah berikut untuk mengonversi paket file HAL menjadi file AIDL:
Bangun alat yang ada di
system/tools/hidl/hidl2aidl.Membangun alat ini dari sumber terbaru memberikan pengalaman yang paling lengkap. Anda dapat menggunakan versi terbaru untuk mengonversi antarmuka di cabang lama dari rilis sebelumnya:
m hidl2aidlJalankan alat dengan direktori output, diikuti dengan paket yang akan dikonversi.
Secara opsional, gunakan argumen
-luntuk menambahkan konten file lisensi baru ke bagian atas semua file yang dihasilkan. Pastikan untuk menggunakan lisensi dan tanggal yang benar:hidl2aidl -o <output directory> -l <file with license> <package>Contoh:
hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2Baca file yang dihasilkan dan perbaiki masalah konversi:
conversion.logberisi masalah yang belum ditangani dan harus diperbaiki terlebih dahulu.- File AIDL yang dihasilkan mungkin memiliki peringatan dan saran yang perlu ditindaklanjuti. Komentar ini diawali dengan
//. - Bersihkan dan lakukan peningkatan pada paket.
- Periksa anotasi
@JavaDeriveuntuk fitur yang mungkin diperlukan, sepertitoStringatauequals.
Buat hanya target yang Anda butuhkan:
- Nonaktifkan backend yang tidak akan digunakan. Lebih memilih backend NDK daripada backend CPP; lihat Membangun terhadap runtime AIDL.
- Hapus library terjemahan atau kode yang dihasilkan yang tidak akan digunakan.
Lihat Perbedaan utama AIDL dan HIDL:
- Penggunaan
Statusdan pengecualian bawaan AIDL biasanya meningkatkan kualitas antarmuka dan menghilangkan kebutuhan akan jenis status khusus antarmuka lainnya. - Argumen antarmuka AIDL dalam metode tidak
@nullablesecara default seperti pada HIDL.
- Penggunaan
SEPolicy untuk HAL AIDL
Jenis layanan AIDL yang terlihat oleh kode vendor harus memiliki atribut
hal_service_type. Jika tidak, konfigurasi sepolicy sama
dengan layanan AIDL lainnya (meskipun ada atribut khusus untuk HAL). Berikut
contoh definisi konteks layanan HAL:
type hal_foo_service, service_manager_type, hal_service_type;
Untuk sebagian besar layanan yang ditentukan oleh platform, konteks layanan dengan jenis yang benar sudah ditambahkan (misalnya, android.hardware.foo.IFoo/default sudah ditandai sebagai hal_foo_service). Namun, jika klien framework mendukung beberapa nama instance, nama instance tambahan harus ditambahkan dalam file service_contexts khusus perangkat:
android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0
Saat membuat jenis HAL baru, Anda harus menambahkan atribut HAL. Atribut HAL
tertentu dapat dikaitkan dengan beberapa jenis layanan (yang masing-masing dapat
memiliki beberapa instance seperti yang baru saja dibahas). Untuk HAL, foo, ada
hal_attribute(foo). Makro ini menentukan atribut hal_foo_client dan
hal_foo_server. Untuk domain tertentu, makro hal_client_domain dan
hal_server_domain mengaitkan domain dengan atribut HAL tertentu. Misalnya, server sistem yang menjadi klien HAL ini sesuai dengan kebijakan hal_client_domain(system_server, hal_foo). Server HAL juga mencakup
hal_server_domain(my_hal_domain, hal_foo).
Biasanya, untuk atribut HAL tertentu, buat juga domain seperti
hal_foo_default untuk referensi atau contoh HAL. Namun, beberapa perangkat menggunakan domain ini untuk servernya sendiri. Membedakan domain untuk
beberapa server hanya penting jika ada beberapa server yang menyajikan
antarmuka yang sama dan memerlukan kumpulan izin yang berbeda dalam implementasinya.
Dalam semua makro ini, hal_foo bukan objek sepolicy. Sebagai gantinya, token ini digunakan oleh makro tersebut untuk merujuk ke grup atribut yang terkait dengan pasangan server klien.
Namun, sejauh ini, hal_foo_service dan hal_foo (pasangan atribut dari
hal_attribute(foo)) belum dikaitkan. Atribut HAL dikaitkan
dengan layanan HAL AIDL menggunakan makro hal_attribute_service (HAL HIDL menggunakan
makro hal_attribute_hwservice), misalnya,
hal_attribute_service(hal_foo, hal_foo_service). Artinya, proses hal_foo_client dapat mengakses HAL, dan proses hal_foo_server dapat mendaftarkan HAL. Penerapan aturan pendaftaran ini dilakukan oleh pengelola konteks (servicemanager).
Nama layanan mungkin tidak selalu sesuai dengan atribut HAL, misalnya,
hal_attribute_service(hal_foo, hal_foo2_service). Secara umum, karena
hal ini menyiratkan bahwa layanan selalu digunakan bersama, Anda dapat menghapus
hal_foo2_service dan menggunakan hal_foo_service untuk semua konteks
layanan. Jika HAL menetapkan beberapa instance hal_attribute_service, hal ini karena nama atribut HAL asli tidak cukup umum dan tidak dapat diubah.
Dengan menggabungkan semuanya, contoh HAL akan terlihat seperti ini:
public/attributes:
// define hal_foo, hal_foo_client, hal_foo_server
hal_attribute(foo)
public/service.te
// define hal_foo_service
type hal_foo_service, hal_service_type, protected_service, service_manager_type
public/hal_foo.te:
// allow binder connection from client to server
binder_call(hal_foo_client, hal_foo_server)
// allow client to find the service, allow server to register the service
hal_attribute_service(hal_foo, hal_foo_service)
// allow binder communication from server to service_manager
binder_use(hal_foo_server)
private/service_contexts:
// bind an AIDL service name to the selinux type
android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0
private/<some_domain>.te:
// let this domain use the hal service
binder_use(some_domain)
hal_client_domain(some_domain, hal_foo)
vendor/<some_hal_server_domain>.te
// let this domain serve the hal service
hal_server_domain(some_hal_server_domain, hal_foo)
Antarmuka ekstensi terlampir
Ekstensi dapat dilampirkan ke antarmuka binder apa pun, baik antarmuka tingkat teratas yang didaftarkan langsung dengan pengelola layanan maupun sub-antarmuka. Saat mendapatkan perpanjangan, Anda harus mengonfirmasi bahwa jenis perpanjangan sesuai dengan yang diharapkan. Anda hanya dapat menyetel ekstensi dari proses yang menayangkan binder.
Gunakan ekstensi terlampir setiap kali ekstensi mengubah fungsi HAL yang ada. Jika kemampuan yang benar-benar baru diperlukan, mekanisme ini tidak diperlukan, dan Anda dapat mendaftarkan antarmuka ekstensi dengan pengelola layanan secara langsung. Antarmuka ekstensi terlampir paling masuk akal jika dilampirkan ke subantarmuka, karena hierarki ini bisa dalam atau multi-instance. Menggunakan ekstensi global untuk mencerminkan hierarki antarmuka binder layanan lain memerlukan pembukuan yang ekstensif untuk menyediakan kemampuan yang setara dengan ekstensi yang terpasang langsung.
Untuk menyetel ekstensi pada binder, gunakan API berikut:
- Backend NDK:
AIBinder_setExtension - Backend Java:
android.os.Binder.setExtension - Backend CPP:
android::Binder::setExtension - Backend Rust:
binder::Binder::set_extension
Untuk mendapatkan ekstensi pada binder, gunakan API berikut:
- Backend NDK:
AIBinder_getExtension - Backend Java:
android.os.IBinder.getExtension - Backend CPP:
android::IBinder::getExtension - Backend Rust:
binder::Binder::get_extension
Anda dapat menemukan informasi selengkapnya untuk API ini dalam dokumentasi fungsi
getExtension di backend yang sesuai. Contoh cara menggunakan
ekstensi ada di
hardware/interfaces/tests/extension/vibrator.
Perbedaan utama AIDL dan HIDL
Saat menggunakan HAL AIDL atau menggunakan antarmuka HAL AIDL, perhatikan perbedaannya dibandingkan dengan menulis HAL HIDL.
- Sintaksis bahasa AIDL lebih mirip dengan Java. Sintaksis HIDL mirip dengan C++.
- Semua antarmuka AIDL memiliki status error bawaan. Daripada membuat jenis status
kustom, buat int status konstan dalam file antarmuka dan gunakan
EX_SERVICE_SPECIFICdi backend CPP dan NDK sertaServiceSpecificExceptiondi backend Java. Lihat Penanganan error. - AIDL tidak otomatis memulai kumpulan thread saat objek binder dikirim. Anda harus memulainya secara manual (lihat Pengelolaan thread).
- AIDL tidak membatalkan error transport yang tidak diperiksa (HIDL
Returnmembatalkan error yang tidak diperiksa). - AIDL hanya dapat mendeklarasikan satu jenis per file.
- Argumen AIDL dapat ditentukan sebagai
in,out, atauinoutselain parameter output (tidak ada callback sinkron). - AIDL menggunakan
fdsebagai jenis primitif, bukanhandle. - HIDL menggunakan versi utama untuk perubahan yang tidak kompatibel dan versi minor untuk
perubahan yang kompatibel. Di AIDL, perubahan yang kompatibel dengan versi lama dilakukan di tempat.
AIDL tidak memiliki konsep eksplisit versi utama; sebagai gantinya, hal ini dimasukkan ke dalam nama paket. Misalnya, AIDL dapat menggunakan nama paket
bluetooth2. - AIDL tidak mewarisi prioritas real-time secara default. Fungsi
setInheritRtharus digunakan per binder untuk mengaktifkan pewarisan prioritas real-time.
Pengujian untuk HAL
Bagian ini menjelaskan praktik terbaik untuk menguji HAL. Praktik ini valid meskipun uji integrasi untuk HAL Anda tidak ada di VTS.
Android mengandalkan VTS untuk memverifikasi implementasi HAL yang diharapkan. VTS membantu memastikan Android dapat kompatibel mundur dengan implementasi vendor lama. Penerapan yang gagal dalam VTS memiliki masalah kompatibilitas yang diketahui yang dapat mencegahnya berfungsi dengan versi OS mendatang.
Ada dua bagian utama VTS untuk HAL.
1. Memverifikasi bahwa HAL di perangkat diketahui dan diharapkan oleh Android
Android mengandalkan daftar statis dan akurat dari semua HAL yang terinstal. Daftar ini dinyatakan dalam Manifes VINTF. Pengujian khusus di seluruh platform memverifikasi integritas lapisan HAL di seluruh sistem secara bersamaan. Sebelum menulis pengujian khusus HAL, Anda juga harus menjalankan pengujian ini, karena pengujian tersebut dapat menunjukkan apakah HAL memiliki konfigurasi VINTF yang tidak konsisten.
Kumpulan pengujian ini dapat ditemukan di
test/vts-testcase/hal/treble/vintf. Jika Anda sedang mengerjakan implementasi HAL vendor, gunakan vts_treble_vintf_vendor_test untuk memverifikasinya. Anda dapat menjalankan
pengujian ini dengan perintah atest vts_treble_vintf_vendor_test.
Pengujian ini bertanggung jawab untuk memverifikasi:
- Setiap antarmuka
@VintfStabilityyang dideklarasikan dalam manifes VINTF dibekukan pada versi rilis yang diketahui. Tindakan ini memverifikasi bahwa kedua sisi antarmuka menyetujui definisi persis dari versi antarmuka tersebut. Hal ini diperlukan untuk operasi dasar. - Semua HAL yang dideklarasikan dalam manifes VINTF tersedia di perangkat tersebut. Klien mana pun dengan izin yang memadai untuk menggunakan layanan HAL yang dideklarasikan harus dapat memperoleh dan menggunakan layanan tersebut kapan saja.
- Semua HAL yang dideklarasikan dalam manifes VINTF menayangkan versi antarmuka yang mereka deklarasikan dalam manifes.
- Tidak ada HAL yang tidak digunakan lagi yang ditayangkan di perangkat. Android menghentikan dukungan untuk antarmuka HAL versi yang lebih rendah seperti yang dijelaskan dalam siklus proses FCM.
- HAL yang diperlukan ada di perangkat. Beberapa HAL diperlukan agar Android berfungsi dengan baik.
2. Verifikasi perilaku yang diharapkan dari setiap HAL
Setiap antarmuka HAL memiliki pengujian VTS sendiri untuk memverifikasi perilaku yang diharapkan dari kliennya. Kasus pengujian dijalankan terhadap setiap instance antarmuka HAL yang dideklarasikan dan menerapkan perilaku tertentu berdasarkan versi antarmuka yang diterapkan.
Di C++, Anda bisa mendapatkan daftar setiap HAL yang diinstal di sistem dengan fungsi android::getAidlHalInstanceNames di libaidlvintf_gtest_helper. Di
Rust, gunakan binder::get_declared_instances.
Pengujian ini berupaya mencakup setiap aspek implementasi HAL yang diandalkan oleh framework Android, atau mungkin diandalkan pada masa mendatang.
Pengujian ini mencakup verifikasi dukungan fitur, penanganan error, dan perilaku lain yang mungkin diharapkan klien dari layanan.
Tonggak pencapaian VTS untuk pengembangan HAL
Pengujian VTS (atau pengujian apa pun) harus selalu diupdate saat membuat atau mengubah antarmuka HAL Android.
Pengujian VTS harus diselesaikan dan siap untuk memverifikasi implementasi vendor sebelum dibekukan untuk rilis Android Vendor API. Mereka harus siap sebelum antarmuka dibekukan sehingga developer dapat membuat implementasinya, memverifikasinya, dan memberikan masukan kepada developer antarmuka HAL.
Pengujian di Cuttlefish
Jika hardware tidak tersedia, Android menggunakan Cuttlefish sebagai sarana pengembangan untuk antarmuka HAL. Hal ini memungkinkan pengujian integrasi Android yang dapat diskalakan.
hal_implementation_test menguji bahwa Cuttlefish memiliki implementasi versi antarmuka HAL terbaru untuk memastikan Android siap menangani antarmuka baru dan pengujian VTS siap menguji implementasi vendor baru segera setelah hardware dan perangkat baru tersedia.