Backend AIDL adalah target pembuatan kode stub. Saat menggunakan file AIDL, Anda selalu menggunakannya dalam bahasa tertentu dengan runtime tertentu. Bergantung pada konteks, Anda harus menggunakan backend AIDL yang berbeda.
Dalam tabel berikut, stabilitas platform API mengacu pada kemampuan
untuk mengompilasi kode terhadap platform API ini dengan cara agar kode dapat
dikirim secara independen dari biner system.img
libbinder.so
.
AIDL memiliki backend berikut:
{i>Backend<i} | Bahasa | Platform API | Sistem build |
---|---|---|---|
Java | Java | SDK/SystemApi (stabil*) | semua |
NDK | C++ | libbinder_ndk (stabil*) | aidl_interface |
CPP | C++ | libbinder (tidak stabil) | semua |
Rust | Rust | libbinder_rs (stabil*) | aidl_interface |
- Platform API ini stabil, tetapi banyak API, seperti API untuk pengelolaan layanan, direservasi untuk penggunaan platform internal dan tidak tersedia untuk aplikasi. Untuk informasi selengkapnya tentang cara menggunakan AIDL dalam aplikasi, lihat dokumentasi developer.
- Backend Rust diperkenalkan di Android 12; backend NDK telah tersedia mulai Android 10.
- Peti Rust dibuat di atas
libbinder_ndk
, yang memungkinkannya untuk stabil dan portabel. APEX menggunakan container binder dengan cara yang sama seperti {i>else<i} di sisi sistem. Bagian Rust digabungkan ke dalam APEX dan dikirim yang ada di dalamnya. Hal ini bergantung padalibbinder_ndk.so
di partisi sistem.
Sistem build
Bergantung pada backend, ada dua cara untuk mengompilasi AIDL ke dalam stub pada kode sumber. Untuk detail selengkapnya tentang sistem build, lihat Referensi Modul Soong.
Sistem build inti
Di modul Android.bp cc_
atau java_
(atau pada Android.mk
yang setara),
File .aidl
dapat ditentukan sebagai file sumber. Dalam hal ini, Java/CPP
yang digunakan adalah backend AIDL (bukan backend NDK), dan class untuk menggunakan
file AIDL yang sesuai akan 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 dalam grup
aidl:
. Perhatikan bahwa backend Rust hanya untuk digunakan dengan Rust. Modul rust_
ditangani secara berbeda karena file AIDL tidak ditentukan sebagai file sumber.
Sebagai gantinya, modul aidl_interface
menghasilkan rustlib
yang disebut
<aidl_interface name>-rust
yang dapat ditautkan. Untuk detail selengkapnya, lihat
contoh Rust AIDL.
aidl_interface
Jenis yang digunakan dengan sistem build ini harus terstruktur. Agar terstruktur, parcelable harus berisi kolom secara langsung dan tidak berupa deklarasi jenis yang didefinisikan langsung dalam bahasa target. Untuk kesesuaian AIDL terstruktur dengan AIDL stabil, lihat AIDL terstruktur versus stabil.
Jenis
Anda dapat menganggap compiler aidl
sebagai implementasi referensi untuk jenis.
Saat Anda membuat antarmuka, panggil aidl --lang=<backend> ...
untuk melihat
file antarmuka yang dihasilkan. Saat menggunakan modul aidl_interface
, Anda dapat melihat
output di out/soong/.intermediates/<path to module>/
.
Jenis Java/AIDL | Jenis C++ | Jenis NDK | Jenis Karat |
---|---|---|---|
boolean | Bool | bool | Bool |
byte | int8_t | int8_t | i8 |
karakter | karakter16 | karakter16 | u16 |
int | int32_t | int32_t | i32 |
long | int64_t | int64_t | i64 |
float | float | float | f32 |
ganda | ganda | ganda | F64 |
String | android::String16 | {i>std::string<i} | String |
android.os.Parcelable | android::Parcelable | T/A | T/A |
IBinder | android::IBinder | ndk::SpAIBinder | binder::SpIBinder |
K[] | std::vektor<T> | std::vector<T> | Masuk: &[T] Keluar: Vec<T> |
byte[] | std::vector<uint8_t> | std::vector<int8_t>1 | Di: &[u8] Keluar: Vec<u8> |
Daftar<T> | std::vector<T>2 | std::vector<T>3 | Masuk: &[T]4 Keluar: Vec<T> |
FileDescriptor | android::base::unique_fd | T/A | binder::parcel::ParcelFileDescriptor |
ParcelFileDescriptor | android::os::ParcelFileDescriptor | ndk::ScopedFileDescriptor | binder::parcel::ParcelFileDescriptor |
jenis antarmuka (T) | android::sp<T> | std::shared_ptr<T>7 | binder::Strong |
jenis parcelable (T) | T | T | T |
jenis union (T)5 | T | T | T |
T[N] 6 | std::array<T, N> | std::array<T, N> | [T; N] |
1. Di Android 12 atau yang lebih tinggi, array byte menggunakan uint8_t, bukan int8_t, karena alasan kompatibilitas.
2. Backend C++ mendukung List<T>
dengan T
adalah salah satu dari String
,
IBinder
, ParcelFileDescriptor
, atau parcelable. Di Android
13 atau yang lebih baru, T
dapat berupa jenis non-primitif
(termasuk jenis antarmuka) kecuali array. AOSP merekomendasikan agar Anda
menggunakan jenis array seperti T[]
, karena jenis ini berfungsi di semua backend.
3. Backend NDK mendukung List<T>
dengan T
adalah salah satu dari String
,
ParcelFileDescriptor
, atau parcelable. Di Android 13
atau yang lebih tinggi, T
dapat berupa jenis non-primitif apa pun kecuali array.
4. Jenis diteruskan secara berbeda untuk kode Rust, bergantung pada apakah jenis tersebut adalah input (argumen), atau output (nilai yang dikembalikan).
5. Jenis gabungan didukung di Android 12 dan lebih tinggi.
6. Di Android 13 atau yang lebih tinggi, array ukuran tetap
didukung. Array berukuran tetap dapat memiliki beberapa dimensi (misalnya, int[3][4]
).
Di backend Java, array berukuran tetap direpresentasikan sebagai jenis array.
7. Untuk membuat instance objek SharedRefBase
binder, gunakan
SharedRefBase::make\<My\>(... args ...)
. Fungsi ini membuat
objek std::shared_ptr\<T\>
yang juga dikelola secara internal, jika binder dimiliki oleh proses
lain. Membuat objek dengan cara lain akan menyebabkan kepemilikan ganda.
Arah (masuk/keluar/masuk-keluar)
Saat menentukan jenis argumen ke fungsi, Anda dapat menentukan
nama tersebut sebagai in
, out
, atau inout
. Ini mengontrol arah informasi
yang diteruskan untuk panggilan IPC. in
adalah arah default, dan ini menunjukkan bahwa data
diteruskan dari pemanggil ke tujuan panggilan. out
berarti data diteruskan dari
pemanggil ke pemanggil. inout
adalah kombinasi dari keduanya. Namun,
tim Android merekomendasikan agar Anda tidak menggunakan penentu argumen inout
.
Jika Anda menggunakan inout
dengan antarmuka berversi dan penggilan yang lebih lama,
kolom tambahan yang hanya ada di pemanggil akan direset ke nilai
defaultnya. Sehubungan dengan Rust, jenis inout
normal menerima &mut Vec<T>
, dan
jenis inout
daftar menerima &mut Vec<T>
.
interface IRepeatExamples {
MyParcelable RepeatParcelable(MyParcelable token); // implicitly 'in'
MyParcelable RepeatParcelableWithIn(in MyParcelable token);
void RepeatParcelableWithInAndOut(in MyParcelable param, out MyParcelable result);
void RepeatParcelableWithInOut(inout MyParcelable param);
}
UTF8/UTF16
Dengan backend CPP, Anda dapat memilih apakah string adalah utf-8 atau utf-16. Deklarasikan
string sebagai @utf8InCpp String
di AIDL untuk mengonversinya secara otomatis ke utf-8.
Backend NDK dan Rust selalu menggunakan string utf-8. Untuk informasi selengkapnya tentang
anotasi utf8InCpp
, lihat Anotasi di AIDL.
Nullability
Anda dapat menganotasi jenis yang boleh bernilai null dengan @nullable
.
Untuk informasi selengkapnya tentang anotasi nullable
, lihat
Anotasi di AIDL.
Parcelable kustom
Parcelable kustom adalah parcelable yang diimplementasikan secara manual di backend target. Gunakan parcelable kustom hanya saat Anda mencoba menambahkan dukungan ke bahasa lain untuk parcelable kustom yang ada dan tidak dapat diubah.
Untuk mendeklarasikan parcelable khusus agar AIDL mengetahuinya, AIDL deklarasi parcelable terlihat seperti ini:
package my.pack.age;
parcelable Foo;
Secara default, ini mendeklarasikan parcelable Java dengan my.pack.age.Foo
adalah class
Java yang mengimplementasikan antarmuka Parcelable
.
Untuk deklarasi backend CPP kustom yang dapat dibagi 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 deklarasi NDK kustom yang dapat dibagi 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
akan 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, untuk deklarasi Rust kustom
yang dapat dipartisi 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 {i>parcelable<i} ini sebagai
jenis dalam file AIDL, tetapi tidak akan
yang dihasilkan oleh AIDL. Menyediakan operator <
dan ==
untuk backend CPP/NDK
parcelable kustom untuk digunakan di union
.
Nilai default
Parcelable terstruktur dapat mendeklarasikan nilai default per kolom untuk primitif,
String
, dan array dari 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 jenis primitif dan null
untuk jenis non-primitif.
Di backend lain, kolom diinisialisasi dengan nilai default yang diinisialisasi saat nilai default tidak ditentukan. Misalnya, di backend C++, kolom String
diinisialisasi sebagai string kosong dan kolom List<T>
diinisialisasi sebagai
vector<T>
kosong. Kolom @nullable
diinisialisasi sebagai kolom nilai null.
Penanganan error
Android OS menyediakan jenis error bawaan yang dapat digunakan layanan saat melaporkan yang sama. Ini digunakan oleh binder dan dapat digunakan oleh layanan apa pun yang mengimplementasikan antarmuka binder. Penggunaannya didokumentasikan dengan baik dalam definisi AIDL dan tidak memerlukan status atau jenis nilai yang ditentukan pengguna.
Parameter output dengan error
Ketika fungsi AIDL melaporkan {i>error<i}, fungsi itu mungkin tidak
memodifikasi parameter output. Secara khusus, parameter output dapat diubah jika
error terjadi selama proses unparceling, bukan terjadi selama pemrosesan
transaksi itu sendiri. Secara umum, ketika menerima pesan {i>
error<i} dari AIDL
semua parameter inout
dan out
serta nilai yang ditampilkan (yang
bertindak seperti parameter out
di beberapa backend) harus dipertimbangkan dalam
status tidak terbatas.
Nilai error yang akan digunakan
Banyak nilai error bawaan yang dapat digunakan di antarmuka AIDL, tetapi beberapa
diperlakukan dengan cara khusus. Misalnya, EX_UNSUPPORTED_OPERATION
dan
EX_ILLEGAL_ARGUMENT
boleh digunakan jika menjelaskan kondisi error, tetapi
EX_TRANSACTION_FAILED
tidak boleh digunakan karena diperlakukan khusus oleh
infrastruktur yang mendasarinya. Periksa definisi khusus backend untuk mengetahui informasi selengkapnya
informasi tentang nilai-nilai bawaan ini.
Jika antarmuka AIDL memerlukan nilai error tambahan yang tidak dicakup oleh
jenis error bawaan, mereka mungkin menggunakan
yang memungkinkan penyertaan nilai
kesalahan khusus layanan yang
ditentukan oleh pengguna. Error khusus layanan ini biasanya ditentukan dalam
antarmuka AIDL sebagai enum
yang didukung const int
atau int
dan tidak diuraikan oleh
binder.
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 native di Android tidak menggunakan pengecualian. Backend CPP menggunakan
android::binder::Status
. Backend NDK menggunakan ndk::ScopedAStatus
. Setiap
yang dihasilkan oleh AIDL akan mengembalikan salah satunya, yang mewakili status
. Backend Rust menggunakan nilai kode pengecualian yang sama seperti NDK, tetapi
mengonversinya menjadi error Rust native (StatusCode
, ExceptionCode
) sebelum
mengirimkannya kepada pengguna. Untuk error khusus layanan, parameter yang ditampilkan
Status
atau ScopedAStatus
menggunakan EX_SERVICE_SPECIFIC
bersama dengan
buatan pengguna.
Jenis error bawaan dapat ditemukan dalam file berikut:
Backend | Definisi |
---|---|
Java | android/os/Parcel.java |
CPP | binder/Status.h |
NDK | android/binder_status.h |
Rust | android/binder_status.h |
Menggunakan berbagai backend
Petunjuk ini khusus untuk kode platform Android. Contoh ini menggunakan
jenis yang ditentukan, my.package.IFoo
. Untuk petunjuk cara menggunakan backend Rust,
lihat contoh AIDL Rust
di halaman
Pola Rust Android.
Jenis impor
Apakah jenis yang ditentukan adalah antarmuka, parcelable, atau union, Anda dapat mengimpor di 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 jenis bertingkat di Java, di backend CPP/NDK, Anda harus
menyertakan header untuk jenis root-nya. Misalnya, saat mengimpor jenis bertingkat
Bar
yang ditentukan di my/package/IFoo.aidl
(IFoo
adalah jenis root
file), Anda harus menyertakan <my/package/IFoo.h>
untuk backend CPP (atau
<aidl/my/package/IFoo.h>
untuk backend NDK).
Menerapkan layanan
Untuk mengimplementasikan layanan, Anda harus mewarisi dari class stub native. Kelas ini membaca perintah dari {i>driver<i} binder dan mengeksekusi metode yang Anda diterapkan. Bayangkan Anda memiliki file AIDL seperti ini:
package my.package;
interface IFoo {
int doFoo();
}
Di Java, Anda harus memperluas dari class 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 asinkron:
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 terdaftar dengan proses
servicemanager
. Selain API di bawah, beberapa API memeriksa
layanan (artinya API akan segera ditampilkan jika layanan tidak tersedia).
Periksa antarmuka servicemanager
yang sesuai untuk detail selengkapnya. Operasi
ini hanya dapat dilakukan saat mengompilasi dengan platform Android.
Di Java:
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 asinkron, 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 kita tidak memanggil
join_thread_pool
saat menggunakan Rust asinkron dan runtime single-thread. Ini adalah
karena Anda perlu memberi Tokio thread tempat ia dapat menjalankan tugas yang dihasilkan. Dalam
contoh ini, thread utama akan memenuhi tujuan tersebut. Setiap tugas yang dihasilkan menggunakan
tokio::spawn
akan dieksekusi di thread utama.
Di backend Rust asinkron, 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-thread, tugas yang dihasilkan tidak dieksekusi di thread
utama. Oleh karena itu, lebih masuk akal untuk memanggil join_thread_pool
di bagian utama
sehingga thread utama tidak hanya tidak ada aktivitas. Anda harus menggabungkan panggilan dalam
block_in_place
untuk keluar dari konteks asinkron.
Tautan menuju kematian
Anda dapat meminta untuk mendapatkan notifikasi saat layanan yang menghosting binder berhenti berfungsi. Hal ini dapat membantu menghindari kebocoran proxy callback atau membantu pemulihan error. Lakukan panggilan ini pada objek proxy binder.
- 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, Anda harus mempertahankan objek tersebut selama Anda ingin menerima notifikasi.
Informasi penelepon
Saat menerima panggilan binder {i>kernel<i}, informasi pemanggil tersedia di beberapa API. PID (atau ID Proses) mengacu pada ID proses Linux dari yang mengirimkan transaksi. UID (atau User-ID) mengacu pada ID pengguna Linux. Saat menerima panggilan satu arah, PID panggilan adalah 0. Saat berada di luar konteks transaksi binder, fungsi ini menampilkan PID dan UID 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 (bukan mengizinkannya secara default):
... = ThreadState::get_calling_pid();
... = ThreadState::get_calling_uid();
Laporan bug dan API proses debug untuk layanan
Saat laporan bug berjalan (misalnya dengan adb bugreport
), mereka mengumpulkan
informasi dari seluruh sistem untuk membantu
proses debug berbagai masalah.
Untuk layanan AIDL, laporan bug menggunakan dumpsys
biner di semua layanan
terdaftar di pengelola layanan untuk memasukkan
informasi mereka ke dalam
laporan {i>bug<i}. Anda juga dapat menggunakan dumpsys
di command line untuk mendapatkan informasi
dari layanan dengan dumpsys SERVICE [ARGS]
. Pada 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 men-debug.
Untuk menambahkan output kustom ke layanan, Anda dapat mengganti dump
di objek server Anda seperti Anda mengimplementasikan metode IPC lainnya
yang ditentukan dalam file AIDL. Saat melakukannya, 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 menerapkan antarmuka, tentukan hal berikut (bukan mengizinkannya menjadi default):
fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>
Menggunakan pointer lemah
Anda dapat menyimpan referensi lemah ke objek binder.
Meskipun mendukung WeakReference
, Java tidak mendukung referensi binder lemah
di lapisan native.
Di backend CPP, jenis kelemahannya adalah wp<IFoo>
.
Di backend NDK, gunakan ScopedAIBinder_Weak
:
#include <android/binder_auto_utils.h>
AIBinder* binder = ...;
ScopedAIBinder_Weak myWeakReference = ScopedAIBinder_Weak(AIBinder_Weak_new(binder));
Di backend Rust, Anda menggunakan WpIBinder
atau Weak<IFoo>
:
let weak_interface = myIface.downgrade();
let weak_binder = myIface.as_binder().downgrade();
Mendapatkan deskriptor antarmuka secara dinamis
Deskripsi antarmuka mengidentifikasi jenis antarmuka. Hal ini berguna saat men-debug atau saat Anda memiliki binder yang tidak diketahui.
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 kemampuan ini.
Mendapatkan deskriptor antarmuka secara statis
Terkadang (seperti saat mendaftarkan layanan @VintfStability
), Anda perlu
mengetahui deskripsi antarmuka secara statis. Di Java, Anda bisa mendapatkan
deskripsi 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 native, Anda dapat mengiterasi kemungkinan nilai yang dapat diambil oleh enum kueri. Karena pertimbangan ukuran kode, skrip ini tidak didukung di Java.
Untuk enum MyEnum
yang ditentukan dalam AIDL, iterasi diberikan sebagai berikut.
Di backend CPP:
::android::enum_range<MyEnum>()
Di backend NDK:
::ndk::enum_range<MyEnum>()
Di backend Rust:
MyEnum::enum_values()
Pengelolaan thread
Setiap instance libbinder
dalam proses mempertahankan satu threadpool. Untuk sebagian besar
, ini harus tepat satu threadpool, yang digunakan bersama di semua backend.
Satu-satunya pengecualian adalah saat kode vendor mungkin memuat salinan libbinder
lainnya
untuk berbicara dengan /dev/vndbinder
. Karena ini adalah simpul binder terpisah, metode
Threadpool tidak dibagikan.
Untuk backend Java, ukuran threadpool hanya dapat ditingkatkan (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 asinkron, Anda memerlukan dua thread pool: binder dan Tokio.
Artinya, aplikasi yang menggunakan Rust asinkron memerlukan pertimbangan khusus,
terutama dalam hal penggunaan join_thread_pool
. Lihat bagian tentang
mendaftarkan layanan untuk mengetahui informasi selengkapnya tentang hal ini.
Nama yang digunakan sistem
C++, Java, dan Rust mencadangkan beberapa nama sebagai kata kunci atau untuk
gunakan. Meskipun AIDL tidak menerapkan batasan berdasarkan aturan bahasa, penggunaan
nama kolom atau jenis yang cocok dengan nama yang dicadangkan dapat menyebabkan kegagalan
kompilasi untuk C++ atau Java. Untuk Rust, kolom atau jenis diganti namanya menggunakan
"raw ID" sintaksis, dapat diakses menggunakan awalan r#
.
Sebaiknya hindari penggunaan nama yang dicadangkan dalam definisi AIDL jika memungkinkan untuk menghindari binding yang tidak ergonomis atau kegagalan kompilasi langsung.
Jika sudah memiliki nama yang dicadangkan dalam definisi AIDL, Anda dapat mengganti nama {i>field<i} namun tetap menggunakan protokol yang kompatibel; Anda mungkin perlu memperbarui kode untuk terus berkembang, tetapi program apa pun yang sudah dibuat akan terus memiliki interoperabilitas.
Nama yang harus dihindari: