Android 11 memperkenalkan kemampuan menggunakan AIDL untuk HAL di Android. Hal ini membuatnya mungkin untuk mengimplementasikan bagian dari Android tanpa HIDL. Transisi HAL untuk menggunakan AIDL secara eksklusif jika memungkinkan (saat HAL upstream menggunakan HIDL, HIDL harus digunakan).
HAL menggunakan AIDL untuk berkomunikasi antar komponen kerangka kerja, seperti yang ada dalam
system.img
, serta 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
mekanisme IPC yang dapat digunakan.
Motivasi
AIDL telah ada lebih lama dari HIDL, dan digunakan di banyak tempat lain, seperti antara komponen framework Android atau dalam aplikasi. Sekarang setelah AIDL memiliki stabilitas dukungan, Anda dapat mengimplementasikan seluruh tumpukan dengan satu waktu proses IPC. AIDL juga memiliki sistem pembuatan versi yang lebih baik daripada HIDL.
- Menggunakan satu bahasa IPC berarti hanya memiliki satu hal untuk dipelajari, di-debug, mengoptimalkan, dan mengamankan.
- AIDL mendukung pembuatan versi di tempat untuk pemilik antarmuka:
- Pemilik dapat menambahkan metode ke akhir antarmuka, atau kolom ke parcelable. Ini berarti lebih mudah untuk membuat kode versi selama bertahun-tahun, dan juga tahun biaya sepanjang tahun lebih kecil (jenis dapat diubah langsung dan tidak ada memerlukan library tambahan untuk setiap versi antarmuka).
- Antarmuka ekstensi dapat dipasang pada runtime, bukan pada jenisnya sehingga tidak perlu me-rebase ekstensi downstream ke dalam setiap versi antarmuka.
- Antarmuka AIDL yang ada dapat digunakan secara langsung ketika pemiliknya memilih untuk menstabilkannya. Sebelumnya, seluruh salinan antarmuka harus yang dibuat di HIDL.
Membangun aplikasi dengan runtime AIDL
AIDL memiliki tiga backend yang berbeda: Java, NDK, CPP. Untuk menggunakan AIDL Stabil, Anda harus
selalu gunakan salinan sistem libbinder di system/lib*/libbinder.so
dan bicara
pada /dev/binder
. Untuk kode pada image vendor, ini berarti bahwa 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 NDK dari
AIDL, penautan ke libbinder_ndk
(yang didukung oleh sistem libbinder.so
),
dan menautkan ke library NDK yang dibuat oleh entri aidl_interface
. Sebagai
nama modul yang tepat, lihat
aturan penamaan modul.
Menulis antarmuka AIDL HAL
Agar antarmuka AIDL dapat digunakan antara sistem dan vendor, antarmuka perlu dua perubahan:
- Setiap definisi jenis harus dianotasi dengan
@VintfStability
. - Deklarasi
aidl_interface
harus menyertakanstability: "vintf",
.
Hanya pemilik antarmuka yang dapat melakukan perubahan ini.
Saat Anda membuat perubahan ini, antarmuka harus berada di
manifes VINTF agar dapat berfungsi. Uji ini (dan yang terkait
persyaratan, seperti memverifikasi bahwa antarmuka yang dirilis telah dibekukan) menggunakan
Uji VTS vts_treble_vintf_vendor_test
. Anda dapat menggunakan @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. Mendowngrade layanan
terhadap stabilitas vendor tidak didukung di Java karena semua aplikasi berjalan di
konteks tambahan.
Selain itu, untuk portabilitas kode yang maksimum dan untuk menghindari potensi masalah seperti sebagai library tambahan yang tidak perlu, nonaktifkan backend CPP.
Perhatikan bahwa penggunaan backends
dalam contoh kode di bawah ini sudah benar, karena
tiga backend (Java, NDK, dan CPP). Kode di bawah ini memberitahukan cara memilih
backend CPP khusus, untuk menonaktifkannya.
aidl_interface: {
...
backends: {
cpp: {
enabled: false,
},
},
}
Menemukan antarmuka AIDL HAL
Antarmuka AIDL Stabil AOSP untuk HAL berada dalam direktori dasar yang sama dengan
Antarmuka HIDL, di folder aidl
.
- hardware/antarmuka
- framework/hardware/antarmuka
- sistem/hardware/antarmuka
Anda harus memasukkan antarmuka ekstensi ke dalam hardware/interfaces
lainnya
subdirektori di vendor
atau hardware
.
Antarmuka ekstensi
Android memiliki serangkaian antarmuka AOSP resmi untuk setiap rilis. Saat Android mitra ingin menambahkan fungsi ke antarmuka ini, mereka tidak boleh berubah ini secara langsung karena ini berarti bahwa runtime Android mereka adalah dan tidak kompatibel dengan runtime Android AOSP. Untuk perangkat GMS, hindari perubahan antarmuka ini yang juga memastikan image GSI dapat terus berfungsi.
Ekstensi dapat didaftarkan dengan dua cara:
- saat runtime, lihat ekstensi yang dilampirkan.
- mandiri, terdaftar secara global, dan di VINTF.
Namun, ekstensi didaftarkan, jika khusus vendor (artinya bukan bagian dari AOSP sebelumnya) menggunakan antarmuka, tidak ada kemungkinan penggabungan konflik. Akan tetapi, ketika modifikasi downstream ke komponen AOSP upstream dilakukan, konflik penggabungan dapat terjadi, dan strategi berikut disarankan:
- penambahan antarmuka dapat di-upstream ke AOSP dalam rilis berikutnya
- penambahan antarmuka yang memungkinkan fleksibilitas lebih lanjut, tanpa konflik penggabungan, dapat di-upstream dalam rilis berikutnya
parcelable ekstensi: ParcelableHolder
ParcelableHolder
adalah Parcelable
yang dapat berisi Parcelable
lainnya.
Kasus penggunaan utama ParcelableHolder
adalah membuat Parcelable
dapat diperluas.
Misalnya, gambar yang diharapkan pengimplementasi perangkat dapat memperluas
Parcelable
, AospDefinedParcelable
yang ditentukan AOSP untuk menyertakan nilai tambahnya
baru.
Sebelumnya tanpa ParcelableHolder
, pengimplementasi perangkat tidak dapat memodifikasi
antarmuka AIDL stabil yang didefinisikan AOSP karena akan menjadi kesalahan untuk menambahkan lebih banyak
bidang:
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 rusak karena kolom ditambahkan oleh pengimplementasi perangkat mungkin mengalami konflik saat Parcelable direvisi dalam rilis Android berikutnya.
Dengan menggunakan ParcelableHolder
, pemilik parcelable dapat menentukan ekstensi
titik di Parcelable
.
parcelable AospDefinedParcelable {
int a;
String b;
ParcelableHolder extension;
}
Selanjutnya, pengimplementasi perangkat dapat menentukan Parcelable
sendiri untuk
.
parcelable OemDefinedParcelable {
String x;
int[] y;
}
Terakhir, Parcelable
yang 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 AIDL HAL
Berdasarkan konvensi, layanan AIDL HAL memiliki nama instance dari format
$package.$type/$instance
. Misalnya, sebuah instance dari vibrator HAL
terdaftar sebagai android.hardware.vibrator.IVibrator/default
.
Menulis server AIDL HAL
@VintfStability
server AIDL harus dideklarasikan dalam manifes VINTF, karena
contoh seperti ini:
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>1</version>
<fqname>IVibrator/default</fqname>
</hal>
Jika tidak, mereka harus mendaftarkan layanan AIDL seperti biasa. Saat menjalankan VTS pengujian, diharapkan semua HAL AIDL yang dideklarasikan tersedia.
Menulis klien AIDL
Klien AIDL harus mendeklarasikan diri mereka sendiri dalam matriks kompatibilitas, misalnya, seperti ini:
<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 menjadi AIDL.
Fitur hidl2aidl
:
- Membuat file
.aidl
berdasarkan file.hal
untuk paket yang ditentukan - Buat aturan build untuk paket AIDL yang baru dibuat dengan semua backend aktif
- Membuat metode terjemahan di backend Java, CPP, dan NDK untuk menerjemahkan dari tipe HIDL hingga tipe AIDL
- Membuat aturan build untuk menerjemahkan library dengan dependensi yang diperlukan
- Membuat pernyataan statis untuk memastikan bahwa 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 terletak di
system/tools/hidl/hidl2aidl
.Membuat alat ini dari sumber terbaru akan memberikan hasil yang paling pengalaman yang lancar bagi developer. Anda dapat menggunakan versi terbaru untuk mengonversi antarmuka pada yang berbeda dari rilis sebelumnya.
m hidl2aidl
Jalankan alat dengan direktori output diikuti dengan paket yang akan dikonversi.
Atau, gunakan argumen
-l
untuk menambahkan konten file lisensi baru di bagian atas semua file yang dihasilkan. Pastikan Anda 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.2
Baca file yang dihasilkan dan perbaiki masalah apa pun pada konversi.
conversion.log
berisi masalah yang tidak tertangani untuk diperbaiki terlebih dahulu.- File
.aidl
yang dihasilkan mungkin memiliki peringatan dan saran yang memerlukan tindakan. Komentar ini diawali dengan//
. - Manfaatkan kesempatan ini untuk membersihkan dan melakukan perbaikan pada paket.
- Periksa
@JavaDerive
anotasi untuk fitur yang mungkin diperlukan, sepertitoString
atauequals
.
Bangun target yang diperlukan saja.
- Nonaktifkan backend yang tidak akan digunakan. Pilih backend NDK daripada CPP backend, lihat memilih runtime.
- Menghapus library terjemahan atau kode yang dihasilkan yang tidak akan digunakan.
Lihat Perbedaan utama AIDL/HIDL.
- Menggunakan
Status
bawaan dan pengecualian AIDL biasanya akan meningkatkan dan menghilangkan kebutuhan akan jenis status khusus antarmuka lainnya. - Argumen antarmuka AIDL dalam metode secara default tidak
@nullable
seperti ini berada di HIDL.
- Menggunakan
SEPolicy untuk AIDL HAL
Jenis layanan AIDL yang terlihat oleh kode vendor harus memiliki
Atribut hal_service_type
. Jika tidak, konfigurasi
sepolicy-nya sama dengan
seperti layanan AIDL lainnya (meskipun ada atribut khusus untuk HAL). Di sini,
adalah contoh definisi konteks layanan HAL:
type hal_foo_service, service_manager_type, hal_service_type;
Untuk sebagian besar layanan yang didefinisikan oleh platform, konteks layanan dengan
jenis sudah ditambahkan (misalnya, android.hardware.foo.IFoo/default
akan
sudah ditandai sebagai hal_foo_service
). Namun, jika kerangka kerja
klien mendukung
lebih dari satu nama instance, nama
instance tambahan harus ditambahkan di
file service_contexts
khusus perangkat.
android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0
Atribut HAL harus ditambahkan saat membuat jenis HAL baru. HAL tertentu
mungkin dikaitkan dengan beberapa jenis layanan (yang masing-masing
memiliki beberapa kasus seperti yang baru saja kita bahas). Untuk HAL, foo
, kita memiliki
hal_attribute(foo)
. Makro ini menentukan atribut hal_foo_client
dan
hal_foo_server
. Untuk domain tertentu, hal_client_domain
dan
makro hal_server_domain
mengaitkan domain dengan atribut HAL yang diberikan. Sebagai
server sistem yang menjadi klien
HAL ini sesuai dengan kebijakan
hal_client_domain(system_server, hal_foo)
. Server HAL juga menyertakan
hal_server_domain(my_hal_domain, hal_foo)
. Biasanya, untuk HAL tertentu
kami juga membuat domain seperti hal_foo_default
untuk referensi atau
contoh HAL. Namun, beberapa perangkat menggunakan domain ini untuk servernya sendiri.
Membedakan antara domain untuk
beberapa server hanya penting jika kita memiliki
beberapa server yang melayani antarmuka yang sama dan memerlukan izin akses yang berbeda
dalam implementasinya. Di semua makro ini, hal_foo
sebenarnya tidak
objek sepolicy. Token ini digunakan oleh makro ini untuk merujuk ke
kelompok atribut yang terkait
dengan pasangan server klien.
Namun, sejauh ini, kita belum mengaitkan hal_foo_service
dan hal_foo
(pasangan atribut dari hal_attribute(foo)
). Atribut HAL dikaitkan
dengan layanan AIDL HAL menggunakan makro hal_attribute_service
(menggunakan HIDL
makro hal_attribute_hwservice
). Misalnya,
hal_attribute_service(hal_foo, hal_foo_service)
. Hal ini berarti bahwa
hal_foo_client
proses dapat memegang HAL, dan hal_foo_server
proses pendaftaran HAL. Pemberlakuan aturan pendaftaran ini
dilakukan oleh pengelola konteks (servicemanager
). Perhatikan, nama layanan mungkin
tidak selalu sesuai dengan
atribut HAL. Sebagai contoh, kita mungkin melihat
hal_attribute_service(hal_foo, hal_foo2_service)
. Umumnya, karena
ini menyiratkan bahwa layanan tersebut selalu digunakan
bersamaan, kita bisa mempertimbangkan untuk menghapus
hal_foo2_service
dan menggunakan hal_foo_service
untuk semua layanan kami
konteks tambahan. Sebagian besar HAL yang menetapkan
beberapa hal_attribute_service
adalah karena
nama atribut HAL asli tidak cukup umum dan tidak dapat diubah.
Dengan menyatukan semuanya, contoh HAL 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 yang disertakan
Ekstensi dapat dipasang ke antarmuka binder apa pun, baik level teratas yang terdaftar langsung dengan pengelola layanan atau merupakan sub-antarmuka. Saat mendapatkan perpanjangan, Anda harus memastikan bahwa jenis ekstensi yang diharapkan. Ekstensi hanya dapat disetel dari proses yang menyajikan binder.
Ekstensi yang dilampirkan harus digunakan setiap kali ekstensi mengubah fungsionalitas HAL yang sudah ada. Ketika fungsionalitas yang benar-benar baru diperlukan, mekanisme ini tidak perlu digunakan, dan antarmuka ekstensi dapat yang terdaftar langsung di {i>service manager<i}. Antarmuka ekstensi yang disertakan paling masuk akal ketika mereka melekat pada sub-antarmuka, karena hierarki mungkin dalam atau multi-instance. Menggunakan ekstensi global untuk mencerminkan hierarki antarmuka {i>binder<i} dari layanan lain akan membutuhkan banyak pembukuan untuk menyediakan fungsi yang setara dengan ekstensi yang dilampirkan langsung.
Untuk menyetel ekstensi pada binder, gunakan API berikut:
- Di backend NDK:
AIBinder_setExtension
- Di backend Java:
android.os.Binder.setExtension
- Di backend CPP:
android::Binder::setExtension
- Di backend Rust:
binder::Binder::set_extension
Untuk mendapatkan ekstensi pada binder, gunakan API berikut:
- Di backend NDK:
AIBinder_getExtension
- Di backend Java:
android.os.IBinder.getExtension
- Di backend CPP:
android::IBinder::getExtension
- Di backend Rust:
binder::Binder::get_extension
Anda bisa menemukan informasi selengkapnya untuk API ini dalam dokumentasi
Fungsi getExtension
di backend terkait. Contoh cara menggunakan
ekstensi dapat ditemukan di
hardware/interfaces/tests/extension/vibrator.
Perbedaan utama AIDL dan HIDL
Saat menggunakan AIDL HAL atau antarmuka AIDL HAL, perhatikan perbedaannya dibandingkan dengan menulis HAL HIDL.
- Sintaks bahasa AIDL lebih dekat dengan Java. Sintaksis HIDL mirip dengan C++.
- Semua antarmuka AIDL memiliki status error bawaan. Daripada membuat
tipe status, buat int status konstan
di file antarmuka dan gunakan
EX_SERVICE_SPECIFIC
di backend CPP/NDK danServiceSpecificException
pada backend Java. Lihat Error Penanganan. - AIDL tidak otomatis memulai thread pool saat objek binder dikirim. Mereka harus dimulai secara manual (lihat rangkaian pesan Google Workspace).
- AIDL tidak dibatalkan saat terjadi error transpor yang tidak dicentang (dibatalkan
Return
HIDL pada error yang tidak dicentang). - AIDL hanya dapat mendeklarasikan satu jenis per file.
- Argumen AIDL dapat ditentukan sebagai in/out/inout selain output (tidak ada "callback sinkron").
- AIDL menggunakan fd sebagai jenis primitif, bukan handle.
- HIDL menggunakan versi utama untuk perubahan yang tidak kompatibel
dan versi minor untuk
perubahan yang kompatibel. Dalam AIDL, perubahan yang kompatibel dengan versi lama dilakukan di tempatnya.
AIDL tidak memiliki konsep eksplisit tentang versi utama; sebagai gantinya,
dimasukkan ke dalam nama paket. Misalnya, AIDL mungkin menggunakan nama paket
bluetooth2
. - AIDL tidak mewarisi prioritas realtime secara default.
setInheritRt
harus digunakan per-binder untuk mengaktifkan pewarisan prioritas realtime.