Android 11 memperkenalkan kemampuan untuk menggunakan AIDL untuk HAL di Android. Hal ini memungkinkan penerapan bagian Android tanpa HIDL. Transisi HAL untuk menggunakan AIDL secara eksklusif jika memungkinkan (ketika HAL hulu menggunakan HIDL, HIDL harus digunakan).
HAL yang menggunakan AIDL untuk berkomunikasi antara komponen kerangka kerja, seperti yang ada di system.img
, dan komponen perangkat keras, seperti yang ada di vendor.img
, harus menggunakan AIDL yang Stabil. Namun, untuk berkomunikasi dalam partisi, misalnya dari satu HAL ke HAL lainnya, tidak ada batasan penggunaan mekanisme IPC.
Motivasi
AIDL telah ada lebih lama dari HIDL, dan digunakan di banyak tempat lain, seperti di antara komponen kerangka kerja Android atau di aplikasi. Sekarang AIDL memiliki dukungan stabilitas, dimungkinkan untuk mengimplementasikan seluruh tumpukan dengan satu runtime IPC. AIDL juga memiliki sistem versi yang lebih baik daripada HIDL.
- Menggunakan satu bahasa IPC berarti hanya memiliki satu hal untuk dipelajari, di-debug, dioptimalkan, dan diamankan.
- AIDL mendukung pembuatan versi di tempat untuk pemilik antarmuka:
- Pemilik dapat menambahkan metode ke akhir antarmuka, atau bidang ke paket. Ini berarti lebih mudah untuk membuat versi kode selama bertahun-tahun, dan juga biaya dari tahun ke tahun lebih kecil (tipe dapat diubah di tempat dan tidak perlu perpustakaan tambahan untuk setiap versi antarmuka).
- Antarmuka ekstensi dapat dilampirkan saat runtime daripada di sistem tipe, jadi tidak perlu mengubah basis 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.
Menulis antarmuka AIDL HAL
Agar antarmuka AIDL dapat digunakan antara sistem dan vendor, antarmuka memerlukan dua perubahan:
- Setiap definisi tipe harus dianotasi dengan
@VintfStability
. - Deklarasi
aidl_interface
harus menyertakanstability: "vintf",
.
Hanya pemilik antarmuka yang dapat melakukan perubahan ini.
Saat Anda melakukan perubahan ini, antarmuka harus ada dalam manifes VINTF agar dapat berfungsi. Uji ini (dan persyaratan terkait, seperti memverifikasi bahwa antarmuka yang dirilis dibekukan) menggunakan uji 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. Mendowngrade layanan ke stabilitas vendor tidak didukung di Java karena semua aplikasi berjalan dalam konteks sistem.
Selain itu, untuk portabilitas kode maksimum dan untuk menghindari kemungkinan masalah seperti pustaka tambahan yang tidak perlu, nonaktifkan backend CPP.
Perhatikan bahwa penggunaan backends
pada contoh kode di bawah sudah benar, karena ada tiga backend (Java, NDK, dan CPP). Kode di bawah ini memberi tahu cara memilih backend CPP secara khusus, untuk menonaktifkannya.
aidl_interface: {
...
backends: {
cpp: {
enabled: false,
},
},
}
Menemukan antarmuka AIDL HAL
Antarmuka AOSP Stable AIDL untuk HAL berada di direktori dasar yang sama dengan antarmuka HIDL, di folder aidl
.
- perangkat keras/antarmuka
- kerangka kerja/perangkat keras/antarmuka
- sistem/perangkat keras/antarmuka
Anda harus meletakkan antarmuka ekstensi ke subdirektori hardware/interfaces
lain di vendor
atau hardware
.
Antarmuka Ekstensi
Android memiliki satu set antarmuka AOSP resmi dengan setiap rilis. Saat mitra Android ingin menambahkan fungsionalitas ke antarmuka ini, mereka tidak boleh mengubahnya secara langsung karena ini berarti waktu proses Android mereka tidak kompatibel dengan waktu proses Android AOSP. Untuk perangkat GMS, menghindari perubahan antarmuka ini juga memastikan image GSI dapat terus berfungsi.
Ekstensi dapat mendaftar dengan dua cara berbeda:
- saat runtime, lihat ekstensi terlampir .
- mandiri, terdaftar secara global dan di VINTF.
Bagaimanapun ekstensi didaftarkan, ketika komponen khusus vendor (artinya bukan bagian dari upstream AOSP) menggunakan antarmuka, tidak ada kemungkinan konflik penggabungan. Namun, ketika modifikasi hilir ke komponen AOSP hulu dibuat, konflik penggabungan dapat terjadi, dan strategi berikut direkomendasikan:
- penambahan antarmuka dapat di-upstream ke AOSP di rilis berikutnya
- penambahan antarmuka yang memungkinkan fleksibilitas lebih lanjut, tanpa konflik penggabungan, dapat di-upstream di rilis berikutnya
Ekstensi Paket: ParcelableHolder
ParcelableHolder
adalah Parcelable
yang dapat berisi Parcelable
lainnya. Kasus penggunaan utama ParcelableHolder
adalah membuat Parcelable
dapat diperluas. Misalnya, gambar yang diharapkan oleh pelaksana perangkat untuk dapat memperluas Parcelable
yang ditentukan AOSP, AospDefinedParcelable
, untuk menyertakan fitur nilai tambah mereka.
Sebelumnya tanpa ParcelableHolder
, pelaksana perangkat tidak dapat memodifikasi antarmuka AIDL stabil yang ditentukan AOSP karena menambahkan lebih banyak kolom akan menjadi kesalahan:
parcelable AospDefinedParcelable {
int a;
String b;
String x; // ERROR: added by a device implementer
int[] y; // added by a device implementer
}
Seperti yang terlihat pada kode sebelumnya, praktik ini rusak karena bidang yang ditambahkan oleh pelaksana perangkat mungkin mengalami konflik saat Parcelable direvisi pada rilis Android berikutnya.
Menggunakan ParcelableHolder
, pemilik parcelable dapat menentukan titik ekstensi dalam Parcelable
.
parcelable AospDefinedParcelable {
int a;
String b;
ParcelableHolder extension;
}
Kemudian pelaksana perangkat dapat menentukan Parcelable
mereka sendiri untuk ekstensi mereka.
parcelable OemDefinedParcelable {
String x;
int[] y;
}
Terakhir, Parcelable
baru dapat dilampirkan ke Parcelable
asli melalui bidang 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>();
Membangun melawan runtime AIDL
AIDL memiliki tiga backend yang berbeda: Java, NDK, CPP. Untuk menggunakan AIDL Stabil, Anda harus selalu menggunakan salinan sistem libbinder di system/lib*/libbinder.so
dan berbicara di /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 asli harus menggunakan backend NDK AIDL, menautkan ke libbinder_ndk
(yang didukung oleh sistem libbinder.so
), dan menautkan ke pustaka -ndk_platform
yang dibuat oleh entri aidl_interface
.
Nama instance server AIDL HAL
Berdasarkan konvensi, layanan AIDL HAL memiliki nama instans dengan format $package.$type/$instance
. Misalnya, instance vibrator HAL didaftarkan sebagai android.hardware.vibrator.IVibrator/default
.
Menulis server AIDL HAL
@VintfStability
Server AIDL harus dideklarasikan dalam manifes VINTF, misalnya 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 secara normal. Saat menjalankan tes VTS, diharapkan semua HAL AIDL yang dideklarasikan tersedia.
Menulis klien AIDL
Klien AIDL harus mendeklarasikan dirinya 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>
Mengubah HAL yang ada dari HIDL ke AIDL
Gunakan alat hidl2aidl
untuk mengonversi antarmuka HIDL ke AIDL.
fitur hidl2aidl
:
- Buat file
.aidl
berdasarkan file.hal
untuk paket yang diberikan - 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 tipe HIDL ke tipe AIDL
- Buat aturan build untuk menerjemahkan library dengan dependensi yang diperlukan
- Buat pernyataan statis untuk memastikan bahwa enumerator HIDL dan AIDL memiliki nilai yang sama di backend CPP dan NDK
Ikuti langkah-langkah ini untuk mengonversi paket file .hal menjadi file .aidl:
Bangun alat yang terletak di
system/tools/hidl/hidl2aidl
.Membangun alat ini dari sumber terbaru memberikan pengalaman terlengkap. Anda dapat menggunakan versi terbaru untuk mengonversi antarmuka di cabang lama dari rilis sebelumnya.
m hidl2aidl
Jalankan alat dengan direktori keluaran diikuti dengan paket yang akan dikonversi.
Secara opsional, gunakan argumen
-l
untuk menambahkan konten file lisensi baru ke atas semua file yang dihasilkan. Pastikan untuk menggunakan lisensi dan tanggal yang benar.hidl2aidl -o <output directory> -l <file with license> <package>
Sebagai contoh:
hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
Baca seluruh file yang dihasilkan dan perbaiki masalah apa pun dengan konversi.
-
conversion.log
berisi masalah yang tidak tertangani untuk diperbaiki terlebih dahulu. - File
.aidl
yang dihasilkan mungkin memiliki peringatan dan saran yang mungkin memerlukan tindakan. Komentar ini dimulai dengan//
. - Manfaatkan kesempatan ini untuk membersihkan dan melakukan perbaikan pada paket.
- Periksa anotasi
@JavaDerive
untuk fitur yang mungkin diperlukan, sepertitoString
atauequals
.
-
Bangun hanya target yang Anda butuhkan.
- Nonaktifkan backend yang tidak akan digunakan. Lebih suka backend NDK daripada backend CPP, lihat memilih runtime .
- Hapus pustaka terjemahan atau kode apa pun yang dihasilkannya yang tidak akan digunakan.
Lihat Perbedaan AIDL/HIDL Utama .
- Menggunakan
Status
dan pengecualian bawaan AIDL biasanya meningkatkan antarmuka dan menghilangkan kebutuhan akan jenis status khusus antarmuka lainnya. - Argumen antarmuka AIDL dalam metode tidak
@nullable
secara default seperti di HIDL.
- Menggunakan
Kebijakan untuk AIDL HALs
Jenis layanan AIDL yang terlihat oleh kode vendor harus memiliki atribut hal_service_type
. Jika tidak, konfigurasi sepolicy sama dengan layanan AIDL lainnya (walaupun ada atribut khusus untuk HAL). Berikut adalah 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 di file service_contexts
khusus perangkat.
android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0
Atribut HAL harus ditambahkan saat kita membuat jenis HAL baru. Atribut HAL tertentu mungkin dikaitkan dengan beberapa jenis layanan (masing-masing mungkin memiliki banyak contoh seperti yang baru saja kita diskusikan). Untuk HAL, foo
, kami memiliki hal_attribute(foo)
. Makro ini mendefinisikan atribut hal_foo_client
dan hal_foo_server
. Untuk domain tertentu, makro hal_client_domain
dan hal_server_domain
mengasosiasikan 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 menyertakan hal_server_domain(my_hal_domain, hal_foo)
. Biasanya, untuk atribut HAL tertentu, kami juga membuat domain seperti hal_foo_default
untuk referensi atau contoh HAL. Namun, beberapa perangkat menggunakan domain ini untuk server mereka sendiri. Membedakan antara domain untuk beberapa server hanya penting jika kita memiliki banyak server yang melayani antarmuka yang sama dan memerlukan izin yang berbeda dalam penerapannya. Di semua makro ini, hal_foo
sebenarnya bukan objek sepolicy. Sebaliknya, token ini digunakan oleh makro ini untuk merujuk ke grup atribut yang terkait dengan pasangan server klien.
Namun, sejauh ini, kami 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
(HAL HIDL menggunakan makro hal_attribute_hwservice
). Misalnya, hal_attribute_service(hal_foo, hal_foo_service)
. Ini berarti bahwa proses hal_foo_client
dapat menguasai HAL, dan proses hal_foo_server
dapat mendaftarkan HAL. Penegakan aturan pendaftaran ini dilakukan oleh manajer konteks ( servicemanager
). Perhatikan, nama layanan mungkin tidak selalu sesuai dengan atribut HAL. Misalnya, kita mungkin melihat hal_attribute_service(hal_foo, hal_foo2_service)
. Namun secara umum, karena ini menyiratkan bahwa layanan selalu digunakan bersama, kami dapat mempertimbangkan untuk menghapus layanan hal_foo2_service
dan menggunakan hal_foo_service
semua konteks layanan kami. Sebagian besar HAL yang mengatur banyak hal_attribute_service
adalah karena nama atribut HAL asli tidak cukup umum dan tidak dapat diubah.
Menyatukan semua ini, 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 terlampir
Ekstensi dapat dilampirkan ke antarmuka pengikat apa pun, apakah itu antarmuka tingkat atas yang terdaftar langsung dengan manajer layanan atau sub-antarmuka. Saat mendapatkan ekstensi, Anda harus memastikan jenis ekstensi sesuai dengan yang diharapkan. Ekstensi hanya dapat diatur dari proses yang melayani pengikat.
Ekstensi terlampir harus digunakan setiap kali ekstensi mengubah fungsionalitas HAL yang ada. Ketika fungsionalitas yang sama sekali baru diperlukan, mekanisme ini tidak perlu digunakan, dan antarmuka ekstensi dapat didaftarkan langsung ke manajer layanan. Antarmuka ekstensi terlampir paling masuk akal saat dilampirkan ke sub-antarmuka, karena hierarki ini mungkin dalam atau multi-instance. Menggunakan ekstensi global untuk mencerminkan hierarki antarmuka pengikat dari layanan lain akan memerlukan pembukuan ekstensif untuk menyediakan fungsionalitas yang setara dengan ekstensi yang terpasang langsung.
Untuk menyetel ekstensi pada pengikat, gunakan API berikut:
- Di backend NDK:
AIBinder_setExtension
- Di backend Java:
android.os.Binder.setExtension
- Di backend CPP:
android::Binder::setExtension
Untuk mendapatkan ekstensi pada pengikat, gunakan API berikut:
- Di backend NDK:
AIBinder_getExtension
- Di backend Java:
android.os.IBinder.getExtension
- Di backend CPP:
android::IBinder::getExtension
Anda dapat 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/HIDL
Saat menggunakan AIDL HAL atau menggunakan antarmuka AIDL HAL, perhatikan perbedaannya dibandingkan dengan menulis HAL HIDL.
- Sintaks bahasa AIDL lebih dekat ke Java. Sintaks HIDL mirip dengan C++.
- Semua antarmuka AIDL memiliki status kesalahan bawaan. Alih-alih membuat tipe status kustom, buat int status konstan dalam file antarmuka dan gunakan
EX_SERVICE_SPECIFIC
di backend CPP/NDK danServiceSpecificException
di backend Java. Lihat Penanganan Kesalahan . - AIDL tidak secara otomatis memulai kumpulan utas saat objek pengikat dikirim. Mereka harus dimulai secara manual (lihat manajemen utas ).
- AIDL tidak membatalkan kesalahan transportasi yang tidak dicentang (
Return
HIDL dibatalkan pada kesalahan yang tidak dicentang). - AIDL hanya dapat mendeklarasikan satu jenis per file.
- Argumen AIDL dapat ditentukan sebagai in/out/inout selain parameter output (tidak ada "callback sinkron").
- AIDL menggunakan fd sebagai tipe primitif, bukan pegangan.
- HIDL menggunakan versi mayor untuk perubahan yang tidak kompatibel dan versi minor untuk perubahan yang kompatibel. Di AIDL, perubahan yang kompatibel dengan mundur dilakukan di tempat. AIDL tidak memiliki konsep eksplisit tentang versi utama; sebagai gantinya, ini dimasukkan ke dalam nama paket. Misalnya, AIDL mungkin menggunakan nama paket
bluetooth2
. - AIDL tidak mewarisi prioritas waktu nyata secara default. Fungsi
setInheritRt
harus digunakan per-binder untuk mengaktifkan pewarisan prioritas waktu nyata.
Android 11 memperkenalkan kemampuan untuk menggunakan AIDL untuk HAL di Android. Hal ini memungkinkan penerapan bagian Android tanpa HIDL. Transisi HAL untuk menggunakan AIDL secara eksklusif jika memungkinkan (ketika HAL hulu menggunakan HIDL, HIDL harus digunakan).
HAL yang menggunakan AIDL untuk berkomunikasi antara komponen kerangka kerja, seperti yang ada di system.img
, dan komponen perangkat keras, seperti yang ada di vendor.img
, harus menggunakan AIDL yang Stabil. Namun, untuk berkomunikasi dalam partisi, misalnya dari satu HAL ke HAL lainnya, tidak ada batasan penggunaan mekanisme IPC.
Motivasi
AIDL telah ada lebih lama dari HIDL, dan digunakan di banyak tempat lain, seperti di antara komponen kerangka kerja Android atau di aplikasi. Sekarang AIDL memiliki dukungan stabilitas, dimungkinkan untuk mengimplementasikan seluruh tumpukan dengan satu runtime IPC. AIDL juga memiliki sistem versi yang lebih baik daripada HIDL.
- Menggunakan satu bahasa IPC berarti hanya memiliki satu hal untuk dipelajari, di-debug, dioptimalkan, dan diamankan.
- AIDL mendukung pembuatan versi di tempat untuk pemilik antarmuka:
- Pemilik dapat menambahkan metode ke akhir antarmuka, atau bidang ke paket. Ini berarti lebih mudah untuk membuat versi kode selama bertahun-tahun, dan juga biaya dari tahun ke tahun lebih kecil (tipe dapat diubah di tempat dan tidak perlu perpustakaan tambahan untuk setiap versi antarmuka).
- Antarmuka ekstensi dapat dilampirkan saat runtime daripada di sistem tipe, jadi tidak perlu mengubah basis 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.
Menulis antarmuka AIDL HAL
Agar antarmuka AIDL dapat digunakan antara sistem dan vendor, antarmuka memerlukan dua perubahan:
- Setiap definisi tipe harus dianotasi dengan
@VintfStability
. - Deklarasi
aidl_interface
harus menyertakanstability: "vintf",
.
Hanya pemilik antarmuka yang dapat melakukan perubahan ini.
Saat Anda melakukan perubahan ini, antarmuka harus ada dalam manifes VINTF agar dapat berfungsi. Uji ini (dan persyaratan terkait, seperti memverifikasi bahwa antarmuka yang dirilis dibekukan) menggunakan uji 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. Mendowngrade layanan ke stabilitas vendor tidak didukung di Java karena semua aplikasi berjalan dalam konteks sistem.
Selain itu, untuk portabilitas kode maksimum dan untuk menghindari kemungkinan masalah seperti pustaka tambahan yang tidak perlu, nonaktifkan backend CPP.
Perhatikan bahwa penggunaan backends
pada contoh kode di bawah sudah benar, karena ada tiga backend (Java, NDK, dan CPP). Kode di bawah ini memberi tahu cara memilih backend CPP secara khusus, untuk menonaktifkannya.
aidl_interface: {
...
backends: {
cpp: {
enabled: false,
},
},
}
Menemukan antarmuka AIDL HAL
Antarmuka AOSP Stable AIDL untuk HAL berada di direktori dasar yang sama dengan antarmuka HIDL, di folder aidl
.
- perangkat keras/antarmuka
- kerangka kerja/perangkat keras/antarmuka
- sistem/perangkat keras/antarmuka
Anda harus meletakkan antarmuka ekstensi ke subdirektori hardware/interfaces
lain di vendor
atau hardware
.
Antarmuka Ekstensi
Android memiliki satu set antarmuka AOSP resmi dengan setiap rilis. Saat mitra Android ingin menambahkan fungsionalitas ke antarmuka ini, mereka tidak boleh mengubahnya secara langsung karena ini berarti waktu proses Android mereka tidak kompatibel dengan waktu proses Android AOSP. Untuk perangkat GMS, menghindari perubahan antarmuka ini juga memastikan image GSI dapat terus berfungsi.
Ekstensi dapat mendaftar dengan dua cara berbeda:
- saat runtime, lihat ekstensi terlampir .
- mandiri, terdaftar secara global dan di VINTF.
Bagaimanapun ekstensi didaftarkan, ketika komponen khusus vendor (artinya bukan bagian dari upstream AOSP) menggunakan antarmuka, tidak ada kemungkinan konflik penggabungan. Namun, ketika modifikasi hilir ke komponen AOSP hulu dibuat, konflik penggabungan dapat terjadi, dan strategi berikut direkomendasikan:
- penambahan antarmuka dapat di-upstream ke AOSP di rilis berikutnya
- penambahan antarmuka yang memungkinkan fleksibilitas lebih lanjut, tanpa konflik penggabungan, dapat di-upstream di rilis berikutnya
Ekstensi Paket: ParcelableHolder
ParcelableHolder
adalah Parcelable
yang dapat berisi Parcelable
lainnya. Kasus penggunaan utama ParcelableHolder
adalah membuat Parcelable
dapat diperluas. Misalnya, gambar yang diharapkan oleh pelaksana perangkat untuk dapat memperluas Parcelable
yang ditentukan AOSP, AospDefinedParcelable
, untuk menyertakan fitur nilai tambah mereka.
Sebelumnya tanpa ParcelableHolder
, pelaksana perangkat tidak dapat memodifikasi antarmuka AIDL stabil yang ditentukan AOSP karena menambahkan lebih banyak kolom akan menjadi kesalahan:
parcelable AospDefinedParcelable {
int a;
String b;
String x; // ERROR: added by a device implementer
int[] y; // added by a device implementer
}
Seperti yang terlihat pada kode sebelumnya, praktik ini rusak karena bidang yang ditambahkan oleh pelaksana perangkat mungkin mengalami konflik saat Parcelable direvisi pada rilis Android berikutnya.
Menggunakan ParcelableHolder
, pemilik parcelable dapat menentukan titik ekstensi dalam Parcelable
.
parcelable AospDefinedParcelable {
int a;
String b;
ParcelableHolder extension;
}
Kemudian pelaksana perangkat dapat menentukan Parcelable
mereka sendiri untuk ekstensi mereka.
parcelable OemDefinedParcelable {
String x;
int[] y;
}
Terakhir, Parcelable
baru dapat dilampirkan ke Parcelable
asli melalui bidang 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>();
Membangun melawan runtime AIDL
AIDL memiliki tiga backend yang berbeda: Java, NDK, CPP. Untuk menggunakan AIDL Stabil, Anda harus selalu menggunakan salinan sistem libbinder di system/lib*/libbinder.so
dan berbicara di /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 asli harus menggunakan backend NDK AIDL, menautkan ke libbinder_ndk
(yang didukung oleh sistem libbinder.so
), dan menautkan ke pustaka -ndk_platform
yang dibuat oleh entri aidl_interface
.
Nama instance server AIDL HAL
Berdasarkan konvensi, layanan AIDL HAL memiliki nama instans dengan format $package.$type/$instance
. Misalnya, instance vibrator HAL didaftarkan sebagai android.hardware.vibrator.IVibrator/default
.
Menulis server AIDL HAL
@VintfStability
Server AIDL harus dideklarasikan dalam manifes VINTF, misalnya 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 secara normal. Saat menjalankan tes VTS, diharapkan semua HAL AIDL yang dideklarasikan tersedia.
Menulis klien AIDL
Klien AIDL harus mendeklarasikan dirinya 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>
Mengubah HAL yang ada dari HIDL ke AIDL
Gunakan alat hidl2aidl
untuk mengonversi antarmuka HIDL ke AIDL.
fitur hidl2aidl
:
- Buat file
.aidl
berdasarkan file.hal
untuk paket yang diberikan - 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 tipe HIDL ke tipe AIDL
- Buat aturan build untuk menerjemahkan library dengan dependensi yang diperlukan
- Buat pernyataan statis untuk memastikan bahwa enumerator HIDL dan AIDL memiliki nilai yang sama di backend CPP dan NDK
Ikuti langkah-langkah ini untuk mengonversi paket file .hal menjadi file .aidl:
Bangun alat yang terletak di
system/tools/hidl/hidl2aidl
.Membangun alat ini dari sumber terbaru memberikan pengalaman terlengkap. Anda dapat menggunakan versi terbaru untuk mengonversi antarmuka di cabang lama dari rilis sebelumnya.
m hidl2aidl
Jalankan alat dengan direktori keluaran diikuti dengan paket yang akan dikonversi.
Secara opsional, gunakan argumen
-l
untuk menambahkan konten file lisensi baru ke atas semua file yang dibuat. Pastikan untuk menggunakan lisensi dan tanggal yang benar.hidl2aidl -o <output directory> -l <file with license> <package>
Sebagai contoh:
hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
Baca seluruh file yang dihasilkan dan perbaiki masalah apa pun dengan konversi.
-
conversion.log
berisi masalah yang tidak tertangani untuk diperbaiki terlebih dahulu. - File
.aidl
yang dihasilkan mungkin memiliki peringatan dan saran yang mungkin memerlukan tindakan. Komentar ini dimulai dengan//
. - Manfaatkan kesempatan ini untuk membersihkan dan melakukan perbaikan pada paket.
- Periksa anotasi
@JavaDerive
untuk fitur yang mungkin diperlukan, sepertitoString
atauequals
.
-
Bangun hanya target yang Anda butuhkan.
- Nonaktifkan backend yang tidak akan digunakan. Lebih suka backend NDK daripada backend CPP, lihat memilih runtime .
- Hapus pustaka terjemahan atau kode apa pun yang dihasilkannya yang tidak akan digunakan.
Lihat Perbedaan AIDL/HIDL Utama .
- Menggunakan
Status
dan pengecualian bawaan AIDL biasanya meningkatkan antarmuka dan menghilangkan kebutuhan akan jenis status khusus antarmuka lainnya. - Argumen antarmuka AIDL dalam metode tidak
@nullable
secara default seperti di HIDL.
- Menggunakan
Kebijakan untuk AIDL HALs
Jenis layanan AIDL yang terlihat oleh kode vendor harus memiliki atribut hal_service_type
. Jika tidak, konfigurasi sepolicy sama dengan layanan AIDL lainnya (walaupun ada atribut khusus untuk HAL). Berikut adalah 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 di file service_contexts
khusus perangkat.
android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0
Atribut HAL harus ditambahkan saat kita membuat jenis HAL baru. Atribut HAL tertentu mungkin dikaitkan dengan beberapa jenis layanan (masing-masing mungkin memiliki banyak contoh seperti yang baru saja kita diskusikan). Untuk HAL, foo
, kami memiliki hal_attribute(foo)
. Makro ini mendefinisikan atribut hal_foo_client
dan hal_foo_server
. Untuk domain tertentu, makro hal_client_domain
dan hal_server_domain
mengasosiasikan 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 menyertakan hal_server_domain(my_hal_domain, hal_foo)
. Biasanya, untuk atribut HAL tertentu, kami juga membuat domain seperti hal_foo_default
untuk referensi atau contoh HAL. Namun, beberapa perangkat menggunakan domain ini untuk server mereka sendiri. Membedakan antara domain untuk beberapa server hanya penting jika kita memiliki banyak server yang melayani antarmuka yang sama dan memerlukan izin yang berbeda dalam penerapannya. Di semua makro ini, hal_foo
sebenarnya bukan objek sepolicy. Sebaliknya, token ini digunakan oleh makro ini untuk merujuk ke grup atribut yang terkait dengan pasangan server klien.
Namun, sejauh ini, kami 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
(HAL HIDL menggunakan makro hal_attribute_hwservice
). Misalnya, hal_attribute_service(hal_foo, hal_foo_service)
. Ini berarti bahwa proses hal_foo_client
dapat menguasai HAL, dan proses hal_foo_server
dapat mendaftarkan HAL. Penegakan aturan pendaftaran ini dilakukan oleh manajer konteks ( servicemanager
). Perhatikan, nama layanan mungkin tidak selalu sesuai dengan atribut HAL. Misalnya, kita mungkin melihat hal_attribute_service(hal_foo, hal_foo2_service)
. Namun secara umum, karena ini menyiratkan bahwa layanan selalu digunakan bersama, kami dapat mempertimbangkan untuk menghapus layanan hal_foo2_service
dan menggunakan hal_foo_service
semua konteks layanan kami. Sebagian besar HAL yang mengatur banyak hal_attribute_service
adalah karena nama atribut HAL asli tidak cukup umum dan tidak dapat diubah.
Menyatukan semua ini, 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 terlampir
Ekstensi dapat dilampirkan ke antarmuka pengikat apa pun, apakah itu antarmuka tingkat atas yang terdaftar langsung dengan manajer layanan atau sub-antarmuka. Saat mendapatkan ekstensi, Anda harus memastikan jenis ekstensi sesuai dengan yang diharapkan. Ekstensi hanya dapat diatur dari proses yang melayani pengikat.
Ekstensi terlampir harus digunakan setiap kali ekstensi mengubah fungsionalitas HAL yang ada. Ketika fungsionalitas yang sama sekali baru diperlukan, mekanisme ini tidak perlu digunakan, dan antarmuka ekstensi dapat didaftarkan langsung ke manajer layanan. Antarmuka ekstensi terlampir paling masuk akal saat dilampirkan ke sub-antarmuka, karena hierarki ini mungkin dalam atau multi-instance. Menggunakan ekstensi global untuk mencerminkan hierarki antarmuka pengikat dari layanan lain akan memerlukan pembukuan ekstensif untuk menyediakan fungsionalitas yang setara dengan ekstensi yang terpasang langsung.
Untuk menyetel ekstensi pada pengikat, gunakan API berikut:
- Di backend NDK:
AIBinder_setExtension
- Di backend Java:
android.os.Binder.setExtension
- Di backend CPP:
android::Binder::setExtension
Untuk mendapatkan ekstensi pada pengikat, gunakan API berikut:
- Di backend NDK:
AIBinder_getExtension
- Di backend Java:
android.os.IBinder.getExtension
- Di backend CPP:
android::IBinder::getExtension
Anda dapat 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/HIDL
Saat menggunakan AIDL HAL atau menggunakan antarmuka AIDL HAL, perhatikan perbedaannya dibandingkan dengan menulis HAL HIDL.
- Sintaks bahasa AIDL lebih dekat ke Java. Sintaks HIDL mirip dengan C++.
- Semua antarmuka AIDL memiliki status kesalahan bawaan. Alih-alih membuat tipe status kustom, buat int status konstan dalam file antarmuka dan gunakan
EX_SERVICE_SPECIFIC
di backend CPP/NDK danServiceSpecificException
di backend Java. Lihat Penanganan Kesalahan . - AIDL tidak secara otomatis memulai kumpulan utas saat objek pengikat dikirim. Mereka harus dimulai secara manual (lihat manajemen utas ).
- AIDL tidak membatalkan kesalahan transportasi yang tidak dicentang (
Return
HIDL dibatalkan pada kesalahan yang tidak dicentang). - AIDL hanya dapat mendeklarasikan satu jenis per file.
- Argumen AIDL dapat ditentukan sebagai in/out/inout selain parameter output (tidak ada "callback sinkron").
- AIDL menggunakan fd sebagai tipe primitif, bukan pegangan.
- HIDL menggunakan versi mayor untuk perubahan yang tidak kompatibel dan versi minor untuk perubahan yang kompatibel. Di AIDL, perubahan yang kompatibel dengan mundur dilakukan di tempat. AIDL tidak memiliki konsep eksplisit tentang versi utama; sebagai gantinya, ini dimasukkan ke dalam nama paket. Misalnya, AIDL mungkin menggunakan nama paket
bluetooth2
. - AIDL tidak mewarisi prioritas waktu nyata secara default. Fungsi
setInheritRt
harus digunakan per-binder untuk mengaktifkan pewarisan prioritas waktu nyata.