AIDL untuk HAL

Android 11 memperkenalkan kemampuan untuk menggunakan AIDL untuk HAL di Android. Hal ini memungkinkan penerapan bagian Android tanpa HIDL. Lakukan transisi HAL untuk menggunakan AIDL secara eksklusif jika memungkinkan (jika HAL upstream menggunakan HIDL, HIDL harus digunakan).

HAL yang menggunakan AIDL untuk berkomunikasi antar-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 lainnya, tidak ada batasan pada mekanisme IPC yang akan digunakan.

Motivasi

AIDL sudah ada lebih lama daripada HIDL, dan digunakan di banyak tempat lain, seperti 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.

  • 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 kolom ke parcelable. Artinya, lebih mudah untuk membuat versi kode dari tahun ke tahun, dan juga biaya dari tahun ke tahun lebih kecil (jenis dapat diubah di tempat dan tidak ada kebutuhan untuk library tambahan untuk setiap versi antarmuka).
    • Antarmuka ekstensi dapat dilampirkan saat runtime, bukan dalam sistem jenis, sehingga tidak perlu memindahkan basis ekstensi downstream ke antarmuka versi 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 berdasarkan 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 berkomunikasi 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 NDK AIDL, menautkan ke libbinder_ndk (yang didukung oleh libbinder.so sistem), dan menautkan 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 dianotasi dengan @VintfStability.
  • Pernyataan aidl_interface harus menyertakan stability: "vintf",.

Hanya pemilik antarmuka yang dapat melakukan perubahan ini.

Saat Anda membuat perubahan ini, antarmuka harus berada dalam manifes VINTF agar berfungsi. Uji hal ini (dan persyaratan terkait, seperti memverifikasi bahwa antarmuka yang dirilis dibekukan) menggunakan pengujian 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 menghindari potensi masalah seperti library tambahan yang tidak diperlukan, nonaktifkan backend CPP.

Perhatikan bahwa penggunaan backends dalam contoh kode di bawah sudah benar, karena ada tiga backend (Java, NDK, dan CPP). Kode di bawah ini menjelaskan cara memilih backend CPP secara khusus, untuk menonaktifkannya.

    aidl_interface: {
        ...
        backends: {
            cpp: {
                enabled: false,
            },
        },
    }

Menemukan antarmuka HAL AIDL

Antarmuka AIDL Stabil AOSP untuk HAL berada di direktori dasar yang sama dengan antarmuka HIDL, di folder aidl.

  • hardware/interfaces: untuk antarmuka yang biasanya disediakan oleh hardware
  • frameworks/hardware/interfaces: untuk antarmuka tingkat tinggi yang disediakan ke hardware
  • system/hardware/interfaces: untuk antarmuka tingkat rendah yang disediakan ke hardware

Anda harus menempatkan antarmuka ekstensi ke dalam subdirektori hardware/interfaces lain di vendor atau hardware.

Antarmuka ekstensi

Android memiliki serangkaian antarmuka AOSP resmi dengan setiap rilis. Saat partner Android ingin menambahkan fungsi ke antarmuka ini, mereka tidak boleh mengubah antarmuka ini secara langsung karena hal ini berarti runtime Android mereka tidak kompatibel dengan runtime Android AOSP. Untuk perangkat GMS, menghindari perubahan antarmuka ini juga memastikan image GSI dapat terus berfungsi.

Ekstensi dapat mendaftar dengan dua cara berbeda:

Namun, ekstensi didaftarkan, saat komponen khusus vendor (artinya bukan bagian dari AOSP upstream) menggunakan antarmuka, tidak ada kemungkinan konflik penggabungan. Namun, saat modifikasi downstream pada komponen AOSP upstream dibuat, konflik penggabungan dapat terjadi, dan strategi berikut direkomendasikan:

  • 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 lain. Kasus penggunaan utama ParcelableHolder adalah membuat Parcelable dapat diperluas. Misalnya, image yang diharapkan oleh pengimplementasi perangkat untuk dapat memperluas Parcelable, AospDefinedParcelable yang ditentukan AOSP, untuk menyertakan fitur nilai tambah mereka.

Sebelumnya tanpa ParcelableHolder, pengimplementasi perangkat tidak dapat mengubah antarmuka AIDL stabil yang ditentukan AOSP karena akan terjadi error jika menambahkan lebih banyak kolom:

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 yang ditambahkan oleh implementator perangkat mungkin memiliki konflik saat Parcelable direvisi dalam rilis Android berikutnya.

Dengan menggunakan ParcelableHolder, pemilik parcelable dapat menentukan titik ekstensi dalam Parcelable.

parcelable AospDefinedParcelable {
  int a;
  String b;
  ParcelableHolder extension;
}

Kemudian, pengimplementasi perangkat dapat menentukan Parcelable-nya sendiri untuk ekstensi mereka.

parcelable OemDefinedParcelable {
  String x;
  int[] y;
}

Terakhir, 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 AIDL HAL

Secara konvensional, layanan HAL AIDL 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 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 pengujian 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>

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 tertentu
  • Membuat aturan build untuk paket AIDL yang baru dibuat dengan semua backend diaktifkan
  • Membuat metode terjemahan di backend Java, CPP, dan NDK untuk menerjemahkan dari jenis HIDL ke jenis AIDL
  • Membuat aturan build untuk menerjemahkan library dengan dependensi yang diperlukan
  • Buat pernyataan statis untuk memastikan bahwa penghitung HIDL dan AIDL memiliki nilai yang sama di backend CPP dan NDK

Ikuti langkah-langkah berikut untuk mengonversi paket file .hal menjadi file .aidl:

  1. Build alat yang terletak di system/tools/hidl/hidl2aidl.

    Mem-build alat ini dari sumber terbaru akan memberikan pengalaman yang paling lengkap. Anda dapat menggunakan versi terbaru untuk mengonversi antarmuka di cabang lama dari rilis sebelumnya.

    m hidl2aidl
  2. Jalankan alat dengan direktori output, diikuti dengan paket yang akan dikonversi.

    Secara opsional, gunakan argumen -l untuk 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.2
  3. Baca file yang dihasilkan dan perbaiki masalah apa pun pada konversi.

    • conversion.log berisi masalah yang tidak ditangani yang harus diperbaiki terlebih dahulu.
    • File .aidl yang dihasilkan mungkin memiliki peringatan dan saran yang mungkin memerlukan tindakan. Komentar ini diawali dengan //.
    • Manfaatkan kesempatan ini untuk membersihkan dan meningkatkan kualitas paket.
    • Periksa anotasi @JavaDerive untuk fitur yang mungkin diperlukan, seperti toString atau equals.
  4. Hanya build target yang Anda perlukan.

    • Nonaktifkan backend yang tidak akan digunakan. Pilih backend NDK daripada backend CPP, lihat memilih runtime.
    • Hapus library terjemahan atau kode yang dihasilkan yang tidak akan digunakan.
  5. Lihat Perbedaan utama AIDL/HIDL.

    • Penggunaan Status bawaan dan pengecualian AIDL biasanya meningkatkan antarmuka dan menghilangkan kebutuhan akan jenis status khusus antarmuka lainnya.
    • Argumen antarmuka AIDL dalam metode tidak berupa @nullable secara default seperti di HIDL.

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 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 akan 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

Atribut HAL harus ditambahkan saat kita membuat jenis HAL baru. Atribut HAL tertentu mungkin dikaitkan dengan beberapa jenis layanan (masing-masing dapat memiliki beberapa instance 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, makro hal_client_domain dan hal_server_domain mengaitkan domain dengan atribut HAL tertentu. Misalnya, server sistem yang merupakan 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, kita 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 beberapa server yang melayani antarmuka yang sama dan memerlukan kumpulan izin yang berbeda dalam penerapannya. Dalam semua makro ini, hal_foo sebenarnya bukan objek sepolicy. Sebagai gantinya, token ini digunakan oleh makro ini untuk merujuk ke grup 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 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 mendapatkan HAL, dan proses hal_foo_server dapat mendaftarkan HAL. Penerapan aturan pendaftaran ini dilakukan oleh pengelola 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 hal ini menyiratkan bahwa layanan selalu digunakan bersama, kita dapat mempertimbangkan untuk menghapus hal_foo2_service dan menggunakan hal_foo_service untuk semua konteks layanan kita. Sebagian besar HAL yang menetapkan beberapa hal_attribute_service 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 terdaftar langsung dengan pengelola layanan maupun sub-antarmuka. Saat mendapatkan ekstensi, Anda harus mengonfirmasi bahwa jenis ekstensi tersebut sesuai dengan yang diharapkan. Ekstensi hanya dapat ditetapkan dari proses yang menayangkan binder.

Ekstensi yang dilampirkan harus digunakan setiap kali ekstensi mengubah fungsi HAL yang ada. Jika fungsi yang sepenuhnya baru diperlukan, mekanisme ini tidak perlu digunakan, dan antarmuka ekstensi dapat didaftarkan langsung ke pengelola layanan. Antarmuka ekstensi yang terpasang paling masuk akal jika dilampirkan ke sub-antarmuka, karena hierarki ini mungkin dalam atau multi-instance. Menggunakan ekstensi global untuk mencerminkan hierarki antarmuka binder dari layanan lain akan memerlukan pencatatan yang ekstensif untuk memberikan fungsi yang setara dengan ekstensi yang terpasang langsung.

Untuk menetapkan 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 dapat menemukan informasi selengkapnya untuk API ini dalam dokumentasi fungsi getExtension di backend yang sesuai. Contoh cara menggunakan ekstensi dapat ditemukan di hardware/interfaces/tests/extension/vibrator.

Perbedaan utama AIDL dan HIDL

Saat menggunakan AIDL HAL atau menggunakan antarmuka AIDL HAL, perhatikan perbedaannya dibandingkan dengan menulis HIDL HAL.

  • 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_SPECIFIC di backend CPP/NDK dan ServiceSpecificException di backend Java. Lihat Penanganan Error.
  • AIDL tidak otomatis memulai threadpool saat objek binder dikirim. Thread harus dimulai secara manual (lihat pengelolaan thread).
  • AIDL tidak dibatalkan saat terjadi error transpor yang tidak dicentang (Return HIDL dibatalkan saat terjadi error 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 jenis primitif, bukan handle.
  • 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 versi utama yang eksplisit; sebagai gantinya, hal ini disertakan dalam nama paket. Misalnya, AIDL mungkin menggunakan nama paket bluetooth2.
  • AIDL tidak mewarisi prioritas real-time secara default. Fungsi setInheritRt harus digunakan per binder untuk mengaktifkan pewarisan prioritas real-time.

Pengujian Vendor Test Suite (VTS) untuk HAL

Android mengandalkan Vendor Test Suite (VTS) untuk memverifikasi implementasi HAL yang diharapkan. VTS membantu memastikan Android dapat kompatibel mundur dengan implementasi vendor lama. Implementasi yang gagal dalam VTS memiliki masalah kompatibilitas umum yang dapat mencegahnya berfungsi dengan versi OS mendatang.

Ada dua bagian utama VTS untuk HAL.

1. Pastikan HAL di perangkat diketahui dan diharapkan oleh Android.

Kumpulan pengujian ini dapat ditemukan di test/vts-testcase/hal/treble/vintf. Mereka bertanggung jawab untuk memverifikasi:

  • Setiap antarmuka @VintfStability yang dideklarasikan dalam manifes VINTF dibekukan pada versi rilis yang diketahui. Hal ini memastikan kedua sisi antarmuka menyetujui definisi yang tepat dari versi antarmuka tersebut. Hal ini diperlukan untuk operasi dasar.
  • Semua HAL yang dideklarasikan dalam manifes VINTF tersedia di perangkat tersebut. Setiap klien dengan izin yang memadai untuk menggunakan layanan HAL yang dideklarasikan harus dapat mendapatkan dan menggunakan layanan tersebut kapan saja.
  • Semua HAL yang dideklarasikan dalam manifes VINTF menayangkan versi antarmuka yang dideklarasikan 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. Memverifikasi perilaku yang diharapkan dari setiap HAL

Setiap antarmuka HAL memiliki pengujian VTS-nya 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 diimplementasikan.

Pengujian ini mencoba mencakup setiap aspek implementasi HAL yang diandalkan 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.

Pencapaian VTS untuk Pengembangan HAL

Pengujian VTS diharapkan selalu diperbarui saat membuat atau mengubah antarmuka HAL Android.

Pengujian VTS harus selesai dan siap untuk memverifikasi implementasi vendor sebelum dibekukan untuk rilis Android Vendor API. Hal ini harus siap sebelum antarmuka dibekukan sehingga developer dapat membuat implementasi, memverifikasinya, dan memberikan masukan kepada developer antarmuka HAL.

VTS di Cuttlefish

Jika hardware tidak tersedia, Android menggunakan Cuttlefish sebagai kendaraan pengembangan untuk antarmuka HAL. Hal ini memungkinkan VTS yang skalabel dan pengujian integrasi Android. 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.