Backend AIDL adalah target pembuatan kode rintisan. Saat menggunakan file AIDL, Anda selalu menggunakannya dalam bahasa tertentu dengan runtime tertentu. Bergantung pada konteksnya, Anda harus menggunakan backend AIDL yang berbeda.
AIDL memiliki backend berikut:
Bagian belakang | Bahasa | permukaan API | Membangun sistem |
---|---|---|---|
Jawa | Jawa | SDK/SystemApi (stabil*) | semua |
NDK | C++ | libbinder_ndk (stabil*) | aidl_interface |
CPP | C++ | libbinder (tidak stabil) | semua |
Karat | Karat | libbinder_rs (tidak stabil) | aidl_interface |
- Permukaan API ini stabil, namun banyak API, seperti API untuk manajemen layanan, dicadangkan untuk penggunaan platform internal dan tidak tersedia untuk aplikasi. Untuk informasi selengkapnya tentang cara menggunakan AIDL di aplikasi, lihat dokumentasi pengembang .
- Backend Rust diperkenalkan di Android 12; backend NDK telah tersedia mulai Android 10.
- Peti Rust dibuat di atas
libbinder_ndk
. APEX menggunakan peti pengikat dengan cara yang sama seperti orang lain di sisi sistem. Bagian Rust dibundel ke dalam APEX dan dikirimkan di dalamnya. Itu tergantung padalibbinder_ndk.so
di partisi sistem.
Membangun sistem
Tergantung pada backendnya, ada dua cara untuk mengkompilasi AIDL menjadi kode rintisan. Untuk detail lebih lanjut tentang sistem pembangunan, lihat Referensi Modul Soong .
Sistem pembangunan inti
Dalam modul Android.bp cc_
atau java_
mana pun (atau yang setara dengan Android.mk
), file .aidl
dapat ditetapkan sebagai file sumber. Dalam hal ini, backend Java/CPP AIDL digunakan (bukan backend NDK), dan kelas yang menggunakan file AIDL terkait ditambahkan ke modul secara otomatis. Opsi seperti local_include_dirs
, yang memberi tahu sistem build jalur root ke file AIDL dalam modul tersebut dapat ditentukan dalam modul ini di bawah grup aidl:
Perhatikan bahwa backend Rust hanya untuk digunakan dengan Rust. modul rust_
ditangani secara berbeda karena file AIDL tidak ditentukan sebagai file sumber. Sebaliknya, modul aidl_interface
menghasilkan rustlib
bernama <aidl_interface name>-rust
yang dapat dihubungkan. Untuk lebih jelasnya lihat contoh Rust AIDL .
aidl_interface
Tipe yang digunakan dengan sistem build ini harus terstruktur. Agar terstruktur, parsel harus berisi bidang secara langsung dan bukan berupa deklarasi tipe yang ditentukan secara langsung dalam bahasa target. Untuk mengetahui kecocokan AIDL terstruktur dengan AIDL stabil, lihat AIDL terstruktur versus stabil .
Jenis
Anda dapat mempertimbangkan kompiler aidl
sebagai implementasi referensi untuk tipe. Saat Anda membuat antarmuka, aktifkan aidl --lang=<backend> ...
untuk melihat file antarmuka yang dihasilkan. Saat Anda menggunakan modul aidl_interface
, Anda dapat melihat output di out/soong/.intermediates/<path to module>/
.
Tipe Java/AIDL | Tipe C++ | Tipe NDK | Jenis Karat |
---|---|---|---|
boolean | bodoh | bodoh | bodoh |
byte | int8_t | int8_t | i8 |
arang | karakter16_t | karakter16_t | u16 |
ke dalam | int32_t | int32_t | i32 |
panjang | int64_t | int64_t | i64 |
mengambang | mengambang | mengambang | f32 |
dobel | dobel | dobel | f64 |
Rangkaian | android::String16 | std::string | Rangkaian |
android.os.Parcelable | android::Dapat diparsel | T/A | T/A |
Pengikat I | android::IBinder | ndk::SpAIBinder | pengikat::SpIBinder |
T[] | std::vektor<T> | std::vektor<T> | Dalam: &[T] Keluar: Vec<T> |
byte[] | std::vektor<uint8_t> | std::vektor<int8_t> 1 | Dalam: &[u8] Keluar: Vec<u8> |
Daftar<T> | std::vektor<T> 2 | std::vektor<T> 3 | Dalam: &[T] 4 Keluar: Vec<T> |
Deskripsi File | android::base::unique_fd | T/A | pengikat::parcel::ParcelFileDescriptor |
Deskriptor ParcelFile | android::os::ParcelFileDescriptor | ndk::ScopedFileDescriptor | pengikat::parcel::ParcelFileDescriptor |
tipe antarmuka (T) | android::sp<T> | std::bersama_ptr<T> | pengikat::Kuat |
tipe parsel (T) | T | T | T |
tipe serikat pekerja (T) 5 | T | T | T |
T[N] 6 | std::array<T, N> | std::array<T, N> | [T; N] |
1. Di Android 12 atau lebih tinggi, array byte menggunakan uint8_t, bukan int8_t, untuk alasan kompatibilitas.
2. Backend C++ mendukung List<T>
di mana T
adalah salah satu dari String
, IBinder
, ParcelFileDescriptor
atau parcelable. Di Android 13 atau lebih tinggi, T
dapat berupa tipe non-primitif apa pun (termasuk tipe antarmuka) kecuali array. AOSP menyarankan Anda menggunakan tipe array seperti T[]
, karena tipe tersebut berfungsi di semua backend.
3. Backend NDK mendukung List<T>
dengan T
adalah salah satu dari String
, ParcelFileDescriptor
, atau parcelable. Di Android 13 atau lebih tinggi, T
dapat berupa tipe non-primitif apa pun kecuali array.
4. Tipe diteruskan secara berbeda untuk kode Rust bergantung pada apakah tipe tersebut merupakan input (argumen), atau output (nilai yang dikembalikan).
5. Jenis gabungan didukung di Android 12 dan lebih tinggi.
6. Di Android 13 atau lebih tinggi, array berukuran tetap didukung. Array berukuran tetap dapat memiliki banyak dimensi (misalnya int[3][4]
). Di backend Java, array berukuran tetap direpresentasikan sebagai tipe array.
Arah (masuk/keluar/masuk)
Saat menentukan tipe argumen pada fungsi, Anda dapat menentukannya sebagai in
, out
, atau inout
. Ini mengontrol ke arah mana informasi diteruskan untuk panggilan IPC. in
adalah arah default, dan ini menunjukkan data diteruskan dari pemanggil ke penerima panggilan. out
berarti data diteruskan dari penerima ke pemanggil. inout
adalah kombinasi keduanya. Namun, tim Android menyarankan agar Anda menghindari penggunaan penentu argumen inout
. Jika Anda menggunakan inout
dengan antarmuka berversi dan callee yang lebih lama, bidang tambahan yang hanya ada di pemanggil akan diatur ulang ke nilai defaultnya. Sehubungan dengan Rust, tipe inout
normal menerima &mut Vec<T>
, dan tipe inout
daftar menerima &mut Vec<T>
.
UTF8/UTF16
Dengan backend CPP Anda dapat memilih apakah stringnya utf-8 atau utf-16. Deklarasikan string sebagai @utf8InCpp String
di AIDL untuk mengonversinya secara otomatis menjadi utf-8. Backend NDK dan Rust selalu menggunakan string utf-8. Untuk informasi selengkapnya tentang anotasi utf8InCpp
, lihat Anotasi di AIDL .
Nullabilitas
Anda dapat menganotasi tipe yang bisa berupa null di backend Java dengan @nullable
untuk mengekspos nilai null ke backend CPP dan NDK. Di backend Rust, tipe @nullable
ini diekspos sebagai Option<T>
. Server asli menolak nilai null secara default. Satu-satunya pengecualian untuk hal ini adalah tipe interface
dan IBinder
, yang selalu bernilai null untuk pembacaan NDK dan penulisan CPP/NDK. Untuk informasi selengkapnya tentang anotasi nullable
, lihat Anotasi di AIDL .
Paket Khusus
Paket khusus adalah paket yang diterapkan secara manual di backend target. Gunakan paket khusus hanya ketika Anda mencoba menambahkan dukungan ke bahasa lain untuk paket khusus yang sudah ada dan tidak dapat diubah.
Untuk mendeklarasikan paket khusus sehingga AIDL mengetahuinya, deklarasi paket AIDL terlihat seperti ini:
package my.pack.age;
parcelable Foo;
Secara default, ini mendeklarasikan Java Parcelable dimana my.pack.age.Foo
adalah kelas Java yang mengimplementasikan antarmuka Parcelable
.
Untuk deklarasi backend CPP khusus yang dapat dipaketkan di AIDL, gunakan cpp_header
:
package my.pack.age;
parcelable Foo cpp_header "my/pack/age/Foo.h";
Implementasi C++ di my/pack/age/Foo.h
terlihat seperti ini:
#include <binder/Parcelable.h>
class MyCustomParcelable : public android::Parcelable {
public:
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
Untuk mendeklarasikan paket NDK khusus di AIDL, gunakan ndk_header
:
package my.pack.age;
parcelable Foo ndk_header "android/pack/age/Foo.h";
Implementasi NDK di android/pack/age/Foo.h
terlihat seperti ini:
#include <android/binder_parcel.h>
class MyCustomParcelable {
public:
binder_status_t writeToParcel(AParcel* _Nonnull parcel) const;
binder_status_t readFromParcel(const AParcel* _Nonnull parcel);
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
Di Android 15 (eksperimen AOSP), untuk mendeklarasikan paket Rust khusus di AIDL, gunakan rust_type
:
package my.pack.age;
@RustOnlyStableParcelable parcelable Foo rust_type "rust_crate::Foo";
Implementasi Rust di rust_crate/src/lib.rs
terlihat seperti ini:
use binder::{
binder_impl::{BorrowedParcel, UnstructuredParcelable},
impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
StatusCode,
};
#[derive(Clone, Debug, Eq, PartialEq)]
struct Foo {
pub bar: String,
}
impl UnstructuredParcelable for Foo {
fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
parcel.write(&self.bar)?;
Ok(())
}
fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
let bar = parcel.read()?;
Ok(Self { bar })
}
}
impl_deserialize_for_unstructured_parcelable!(Foo);
impl_serialize_for_unstructured_parcelable!(Foo);
Kemudian Anda dapat menggunakan parselable ini sebagai tipe dalam file AIDL, tetapi ini tidak akan dihasilkan oleh AIDL. Sediakan operator <
dan ==
untuk paket khusus backend CPP/NDK untuk digunakan dalam union
.
Nilai dasar
Parcelable terstruktur dapat mendeklarasikan nilai default per kolom untuk primitif, String
, dan array jenis ini.
parcelable Foo {
int numField = 42;
String stringField = "string value";
char charValue = 'a';
...
}
Di backend Java ketika nilai default tidak ada, bidang diinisialisasi sebagai nilai nol untuk tipe primitif dan null
untuk tipe non-primitif.
Di backend lain, bidang diinisialisasi dengan nilai inisialisasi default ketika nilai default tidak ditentukan. Misalnya, di backend C++, kolom String
diinisialisasi sebagai string kosong dan kolom List<T>
diinisialisasi sebagai vector<T>
kosong. Bidang @nullable
diinisialisasi sebagai bidang nilai nol.
Penanganan Kesalahan
OS Android menyediakan jenis kesalahan bawaan untuk digunakan layanan saat melaporkan kesalahan. Ini digunakan oleh pengikat dan dapat digunakan oleh layanan apa pun yang mengimplementasikan antarmuka pengikat. Penggunaannya terdokumentasi dengan baik dalam definisi AIDL dan tidak memerlukan status atau jenis pengembalian apa pun yang ditentukan pengguna.
Parameter keluaran dengan kesalahan
Ketika fungsi AIDL melaporkan kesalahan, fungsi tersebut mungkin tidak menginisialisasi atau mengubah parameter output. Secara khusus, parameter output dapat diubah jika kesalahan terjadi selama proses unparceling dan bukan terjadi selama pemrosesan transaksi itu sendiri. Secara umum, saat mendapatkan kesalahan dari fungsi AIDL, semua parameter inout
dan out
serta nilai yang dikembalikan (yang berfungsi seperti parameter out
di beberapa backend) harus dianggap berada dalam keadaan tidak terbatas.
Nilai kesalahan mana yang akan digunakan
Banyak nilai kesalahan bawaan yang dapat digunakan di antarmuka AIDL apa pun, namun ada pula yang diperlakukan dengan cara khusus. Misalnya, EX_UNSUPPORTED_OPERATION
dan EX_ILLEGAL_ARGUMENT
boleh digunakan saat menjelaskan kondisi kesalahan, namun EX_TRANSACTION_FAILED
tidak boleh digunakan karena diperlakukan khusus oleh infrastruktur yang mendasarinya. Periksa definisi spesifik backend untuk informasi selengkapnya tentang nilai bawaan ini.
Jika antarmuka AIDL memerlukan nilai kesalahan tambahan yang tidak tercakup dalam jenis kesalahan bawaan, maka antarmuka AIDL dapat menggunakan kesalahan bawaan khusus layanan yang memungkinkan penyertaan nilai kesalahan khusus layanan yang ditentukan oleh pengguna. . Kesalahan khusus layanan ini biasanya ditentukan di antarmuka AIDL sebagai enum
yang didukung const int
atau int
dan tidak diuraikan oleh pengikat.
Di Java, error dipetakan ke pengecualian, seperti android.os.RemoteException
. Untuk pengecualian khusus layanan, Java menggunakan android.os.ServiceSpecificException
bersama dengan error yang ditentukan pengguna.
Kode asli di Android tidak menggunakan pengecualian. Backend CPP menggunakan android::binder::Status
. Backend NDK menggunakan ndk::ScopedAStatus
. Setiap metode yang dihasilkan oleh AIDL mengembalikan salah satu metode ini, yang mewakili status metode tersebut. Backend Rust menggunakan nilai kode pengecualian yang sama dengan NDK, namun mengubahnya menjadi error Rust asli ( StatusCode
, ExceptionCode
) sebelum mengirimkannya ke pengguna. Untuk kesalahan khusus layanan, Status
atau ScopedAStatus
yang dikembalikan menggunakan EX_SERVICE_SPECIFIC
bersama dengan kesalahan yang ditentukan pengguna.
Jenis kesalahan bawaan dapat ditemukan di file berikut:
Bagian belakang | Definisi |
---|---|
Jawa | android/os/Parcel.java |
CPP | binder/Status.h |
NDK | android/binder_status.h |
Karat | android/binder_status.h |
Menggunakan berbagai backend
Petunjuk ini khusus untuk kode platform Android. Contoh-contoh ini menggunakan tipe yang ditentukan, my.package.IFoo
. Untuk petunjuk tentang cara menggunakan backend Rust, lihat contoh Rust AIDL di halaman Android Rust Patterns .
Mengimpor jenis
Baik tipe yang ditentukan adalah antarmuka, paket, atau gabungan, Anda dapat mengimpornya ke Java:
import my.package.IFoo;
Atau di backend CPP:
#include <my/package/IFoo.h>
Atau di backend NDK (perhatikan namespace aidl
tambahan):
#include <aidl/my/package/IFoo.h>
Atau di backend Rust:
use my_package::aidl::my::package::IFoo;
Meskipun Anda dapat mengimpor tipe bertingkat di Java, di backend CPP/NDK Anda harus menyertakan header untuk tipe rootnya. Misalnya, saat mengimpor Bar
tipe bersarang yang ditentukan di my/package/IFoo.aidl
( IFoo
adalah tipe root file), Anda harus menyertakan <my/package/IFoo.h>
untuk backend CPP (atau <aidl/my/package/IFoo.h>
untuk backend NDK).
Layanan implementasi
Untuk mengimplementasikan layanan, Anda harus mewarisi kelas stub asli. Kelas ini membaca perintah dari driver binder dan mengeksekusi metode yang Anda terapkan. Bayangkan Anda memiliki file AIDL seperti ini:
package my.package;
interface IFoo {
int doFoo();
}
Di Java, Anda harus memperluas dari kelas ini:
import my.package.IFoo;
public class MyFoo extends IFoo.Stub {
@Override
int doFoo() { ... }
}
Di backend CPP:
#include <my/package/BnFoo.h>
class MyFoo : public my::package::BnFoo {
android::binder::Status doFoo(int32_t* out) override;
}
Di backend NDK (perhatikan namespace aidl
tambahan):
#include <aidl/my/package/BnFoo.h>
class MyFoo : public aidl::my::package::BnFoo {
ndk::ScopedAStatus doFoo(int32_t* out) override;
}
Di backend Rust:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFoo};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
impl IFoo for MyFoo {
fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
Atau dengan Rust async:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFooAsyncServer};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
#[async_trait]
impl IFooAsyncServer for MyFoo {
async fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
Mendaftar dan mendapatkan layanan
Layanan di platform Android biasanya didaftarkan pada proses servicemanager
. Selain API di bawah, beberapa API memeriksa layanan (artinya API tersebut segera kembali jika layanan tidak tersedia). Periksa antarmuka servicemanager
yang sesuai untuk detail selengkapnya. Operasi ini hanya dapat dilakukan saat kompilasi terhadap platform Android.
Di Jawa:
import android.os.ServiceManager;
// registering
ServiceManager.addService("service-name", myService);
// return if service is started now
myService = IFoo.Stub.asInterface(ServiceManager.checkService("service-name"));
// waiting until service comes up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForService("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForDeclaredService("service-name"));
Di backend CPP:
#include <binder/IServiceManager.h>
// registering
defaultServiceManager()->addService(String16("service-name"), myService);
// return if service is started now
status_t err = checkService<IFoo>(String16("service-name"), &myService);
// waiting until service comes up (new in Android 11)
myService = waitForService<IFoo>(String16("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = waitForDeclaredService<IFoo>(String16("service-name"));
Di backend NDK (perhatikan namespace aidl
tambahan):
#include <android/binder_manager.h>
// registering
binder_exception_t err = AServiceManager_addService(myService->asBinder().get(), "service-name");
// return if service is started now
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_checkService("service-name")));
// is a service declared in the VINTF manifest
// VINTF services have the type in the interface instance name.
bool isDeclared = AServiceManager_isDeclared("android.hardware.light.ILights/default");
// wait until a service is available (if isDeclared or you know it's available)
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_waitForService("service-name")));
Di backend Rust:
use myfoo::MyFoo;
use binder;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_binder(
my_service,
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Does not return - spawn or perform any work you mean to do before this call.
binder::ProcessState::join_thread_pool()
}
Di backend Rust async, dengan runtime thread tunggal:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "current_thread")]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleeps forever, but does not join the binder threadpool.
// Spawned tasks will run on this thread.
std::future::pending().await
}
Satu perbedaan penting dari opsi lainnya adalah kami tidak memanggil join_thread_pool
saat menggunakan async Rust dan runtime thread tunggal. Ini karena Anda perlu memberi Tokio thread yang dapat menjalankan tugas yang muncul. Dalam contoh ini, thread utama akan melayani tujuan tersebut. Setiap tugas yang dihasilkan menggunakan tokio::spawn
akan dijalankan di thread utama.
Di backend Rust async, dengan runtime multi-thread:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleep forever.
tokio::task::block_in_place(|| {
binder::ProcessState::join_thread_pool();
});
}
Dengan runtime Tokio multi-utas, tugas yang dihasilkan tidak dijalankan di thread utama. Oleh karena itu, lebih masuk akal untuk memanggil join_thread_pool
pada thread utama agar thread utama tidak hanya menganggur. Anda harus menyelesaikan panggilan di block_in_place
untuk keluar dari konteks async.
Menghubungkan dengan kematian
Anda dapat meminta untuk mendapatkan pemberitahuan ketika layanan yang menghosting pengikat mati. Hal ini dapat membantu menghindari kebocoran proxy panggilan balik atau membantu pemulihan kesalahan. Lakukan panggilan ini pada objek proxy pengikat.
- Di Java, gunakan
android.os.IBinder::linkToDeath
. - Di backend CPP, gunakan
android::IBinder::linkToDeath
. - Di backend NDK, gunakan
AIBinder_linkToDeath
. - Di backend Rust, buat objek
DeathRecipient
, lalu panggilmy_binder.link_to_death(&mut my_death_recipient)
. Perhatikan bahwa karenaDeathRecipient
memiliki callback tersebut, Anda harus menjaga objek tersebut tetap hidup selama Anda ingin menerima notifikasi.
Informasi Penelepon
Saat menerima panggilan pengikat kernel, informasi pemanggil tersedia di beberapa API. PID (atau ID Proses) mengacu pada ID proses Linux dari proses yang mengirimkan transaksi. UID (atau ID Pengguna) mengacu pada ID pengguna Linux. Saat menerima panggilan satu arah, PID pemanggil adalah 0. Ketika berada di luar konteks transaksi pengikat, fungsi ini mengembalikan PID dan UID dari proses saat ini.
Di backend Java:
... = Binder.getCallingPid();
... = Binder.getCallingUid();
Di backend CPP:
... = IPCThreadState::self()->getCallingPid();
... = IPCThreadState::self()->getCallingUid();
Di backend NDK:
... = AIBinder_getCallingPid();
... = AIBinder_getCallingUid();
Di backend Rust, saat mengimplementasikan antarmuka, tentukan hal berikut (alih-alih membiarkannya default):
... = ThreadState::get_calling_pid();
... = ThreadState::get_calling_uid();
Laporan bug dan API debugging untuk layanan
Saat laporan bug dijalankan (misalnya, dengan adb bugreport
), laporan bug tersebut mengumpulkan informasi dari seluruh sistem untuk membantu proses debug berbagai masalah. Untuk layanan AIDL, laporan bug menggunakan dumpsys
biner pada semua layanan yang terdaftar pada manajer layanan untuk membuang informasinya ke dalam laporan bug. Anda juga dapat menggunakan dumpsys
pada baris perintah untuk mendapatkan informasi dari layanan dengan dumpsys SERVICE [ARGS]
. Di backend C++ dan Java, Anda dapat mengontrol urutan pembuangan layanan dengan menggunakan argumen tambahan ke addService
. Anda juga dapat menggunakan dumpsys --pid SERVICE
untuk mendapatkan PID layanan saat melakukan debug.
Untuk menambahkan keluaran khusus ke layanan Anda, Anda dapat mengganti metode dump
di objek server Anda seperti Anda menerapkan metode IPC lain yang ditentukan dalam file AIDL. Saat melakukan ini, Anda harus membatasi pembuangan ke izin aplikasi android.permission.DUMP
atau membatasi pembuangan ke UID tertentu.
Di backend Java:
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {...}
Di backend CPP:
status_t dump(int, const android::android::Vector<android::String16>&) override;
Di backend NDK:
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
Di backend Rust, saat mengimplementasikan antarmuka, tentukan hal berikut (alih-alih membiarkannya default):
fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>
Mendapatkan deskriptor antarmuka secara dinamis
Deskriptor antarmuka mengidentifikasi jenis antarmuka. Ini berguna saat melakukan debug atau saat Anda memiliki pengikat yang tidak dikenal.
Di Java, Anda bisa mendapatkan deskriptor antarmuka dengan kode seperti:
service = /* get ahold of service object */
... = service.asBinder().getInterfaceDescriptor();
Di backend CPP:
service = /* get ahold of service object */
... = IInterface::asBinder(service)->getInterfaceDescriptor();
Backend NDK dan Rust tidak mendukung fungsi ini.
Mendapatkan deskriptor antarmuka secara statis
Terkadang (seperti saat mendaftarkan layanan @VintfStability
), Anda perlu mengetahui deskriptor antarmuka secara statis. Di Java, Anda bisa mendapatkan deskriptor dengan menambahkan kode seperti:
import my.package.IFoo;
... IFoo.DESCRIPTOR
Di backend CPP:
#include <my/package/BnFoo.h>
... my::package::BnFoo::descriptor
Di backend NDK (perhatikan namespace aidl
tambahan):
#include <aidl/my/package/BnFoo.h>
... aidl::my::package::BnFoo::descriptor
Di backend Rust:
aidl::my::package::BnFoo::get_descriptor()
Rentang Enum
Di backend asli, Anda dapat mengulangi kemungkinan nilai yang dapat diambil oleh enum. Karena pertimbangan ukuran kode, saat ini hal ini tidak didukung di Java.
Untuk enum MyEnum
yang ditentukan dalam AIDL, iterasi disediakan sebagai berikut.
Di backend CPP:
::android::enum_range<MyEnum>()
Di backend NDK:
::ndk::enum_range<MyEnum>()
Di backend Rust:
MyEnum::enum_values()
Manajemen benang
Setiap contoh libbinder
dalam suatu proses memelihara satu threadpool. Untuk sebagian besar kasus penggunaan, ini harus berupa satu threadpool, yang dibagikan ke semua backend. Satu-satunya pengecualian untuk hal ini adalah ketika kode vendor mungkin memuat salinan libbinder
lain untuk diajak bicara /dev/vndbinder
. Karena ini berada pada node pengikat terpisah, threadpool tidak dibagikan.
Untuk backend Java, threadpool hanya dapat bertambah ukurannya (karena sudah dimulai):
BinderInternal.setMaxThreads(<new larger value>);
Untuk backend CPP, operasi berikut tersedia:
// set max threadpool count (default is 15)
status_t err = ProcessState::self()->setThreadPoolMaxThreadCount(numThreads);
// create threadpool
ProcessState::self()->startThreadPool();
// add current thread to threadpool (adds thread to max thread count)
IPCThreadState::self()->joinThreadPool();
Demikian pula, di backend NDK:
bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
ABinderProcess_startThreadPool();
ABinderProcess_joinThreadPool();
Di backend Rust:
binder::ProcessState::start_thread_pool();
binder::add_service(“myservice”, my_service_binder).expect(“Failed to register service?”);
binder::ProcessState::join_thread_pool();
Dengan backend Rust async, Anda memerlukan dua threadpool: binder dan Tokio. Artinya, aplikasi yang menggunakan async Rust memerlukan pertimbangan khusus, terutama saat menggunakan join_thread_pool
. Lihat bagian pendaftaran layanan untuk informasi lebih lanjut mengenai hal ini.
Nama yang Dicadangkan
C++, Java, dan Rust mencadangkan beberapa nama sebagai kata kunci atau untuk penggunaan bahasa tertentu. Meskipun AIDL tidak menerapkan pembatasan berdasarkan aturan bahasa, penggunaan nama bidang atau tipe yang cocok dengan nama yang dicadangkan mungkin mengakibatkan kegagalan kompilasi untuk C++ atau Java. Untuk Rust, bidang atau tipe diganti namanya menggunakan sintaksis "pengidentifikasi mentah", yang dapat diakses menggunakan awalan r#
.
Kami menyarankan Anda menghindari penggunaan nama khusus dalam definisi AIDL Anda jika memungkinkan untuk menghindari pengikatan yang tidak ergonomis atau kegagalan kompilasi langsung.
Jika Anda sudah memiliki nama khusus dalam definisi AIDL, Anda dapat dengan aman mengganti nama bidang namun tetap kompatibel dengan protokol; Anda mungkin perlu memperbarui kode Anda untuk terus membangun, tetapi program apa pun yang sudah dibangun akan terus beroperasi.
Nama yang harus dihindari: * Kata Kunci C++ * Kata Kunci Java * Kata Kunci Rust