AIDL arka ucu, saplama kodu oluşturmaya yönelik bir hedeftir. AIDL dosyalarını kullanırken, bunları her zaman belirli bir dilde, belirli bir çalışma zamanıyla kullanırsınız. Bağlama bağlı olarak farklı AIDL arka uçları kullanmalısınız.
Aşağıdaki tabloda, API yüzeyinin kararlılığı, kodun system.img
libbinder.so
ikili dosyasından bağımsız olarak teslim edilebilecek şekilde bu API yüzeyine karşı kod derleme yeteneğini ifade eder.
AIDL aşağıdaki arka uçlara sahiptir:
Arka uç | Dil | API yüzeyi | Sistemler oluşturun |
---|---|---|---|
Java | Java | SDK/SystemApi (kararlı*) | Tümü |
NDK | C++ | libinder_ndk (kararlı*) | yardım_arayüzü |
CPP | C++ | libbinder (kararsız) | Tümü |
Pas | Pas | libinder_rs (kararlı*) | yardım_arayüzü |
- Bu API yüzeyleri kararlıdır ancak hizmet yönetimine yönelik olanlar gibi API'lerin çoğu dahili platform kullanımına ayrılmıştır ve uygulamalar tarafından kullanılamaz. AIDL'nin uygulamalarda nasıl kullanılacağı hakkında daha fazla bilgi için geliştirici belgelerine bakın.
- Rust arka ucu Android 12'de tanıtıldı; NDK arka ucu Android 10'dan itibaren kullanıma sunuldu.
- Rust sandığı
libbinder_ndk
üzerine inşa edilmiştir, bu da onun sağlam ve taşınabilir olmasını sağlar. APEX'ler ciltleme sandığını sistem tarafındaki diğer herkesle aynı şekilde kullanır. Rust kısmı bir APEX'e paketlenir ve onun içinde gönderilir. Sistem bölümündekilibbinder_ndk.so
dosyasına bağlıdır.
Sistemler oluşturun
Arka uca bağlı olarak AIDL'yi saplama kodu halinde derlemenin iki yolu vardır. Yapı sistemleri hakkında daha fazla ayrıntı için Soong Modül Referansı'na bakın.
Çekirdek yapı sistemi
Herhangi bir cc_
veya java_
Android.bp modülünde (veya bunların Android.mk
eşdeğerlerinde), .aidl
dosyaları kaynak dosyalar olarak belirtilebilir. Bu durumda, AIDL'nin Java/CPP arka uçları kullanılır (NDK arka ucu değil) ve karşılık gelen AIDL dosyalarını kullanacak sınıflar, modüle otomatik olarak eklenir. Yapı sistemine söz konusu modüldeki AIDL dosyalarının kök yolunu bildiren local_include_dirs
gibi seçenekler, bu modüllerde bir aidl:
grubu altında belirtilebilir. Rust arka ucunun yalnızca Rust ile kullanıma yönelik olduğunu unutmayın. rust_
modülleri, AIDL dosyalarının kaynak dosyalar olarak belirtilmemesi nedeniyle farklı şekilde işlenir. Bunun yerine aidl_interface
modülü, bağlantı kurulabilecek <aidl_interface name>-rust
adlı bir rustlib
üretir. Daha fazla ayrıntı için Rust AIDL örneğine bakın.
aidl_interface
Bu yapı sistemiyle kullanılan türler yapılandırılmış olmalıdır. Yapılandırılabilmesi için parsellenebilirlerin doğrudan alanları içermesi ve doğrudan hedef dillerde tanımlanan türlerin bildirimleri olmaması gerekir. Yapılandırılmış AIDL'nin kararlı AIDL'ye nasıl uyduğunu öğrenmek için bkz. Yapılandırılmış ve kararlı AIDL'ye karşı .
Türler
aidl
derleyicisini türler için referans uygulaması olarak düşünebilirsiniz. Bir arayüz oluşturduğunuzda, ortaya çıkan arayüz dosyasını görmek için aidl --lang=<backend> ...
komutunu çağırın. aidl_interface
modülünü kullandığınızda out/soong/.intermediates/<path to module>/
içindeki çıktıyı görüntüleyebilirsiniz.
Java/AIDL Türü | C++ Türü | NDK Tipi | Pas Tipi |
---|---|---|---|
boolean | bool | bool | bool |
bayt | int8_t | int8_t | i8 |
karakter | karakter16_t | karakter16_t | u16 |
int | int32_t | int32_t | i32 |
uzun | int64_t | int64_t | i64 |
batmadan yüzmek | batmadan yüzmek | batmadan yüzmek | f32 |
çift | çift | çift | f64 |
Sicim | android::String16 | std::dize | Sicim |
android.os.Parcellenebilir | android::Parsellenebilir | Yok | Yok |
IBinder | android::IBinder | ndk::SpAIBinder | bağlayıcı::SpIBinder |
T[] | std::vector<T> | std::vector<T> | İçinde: &[T] Dışarı: Vec<T> |
bayt[] | std::vector<uint8_t> | std::vector<int8_t> 1 | İçinde: &[u8] Dışarı: Vec<u8> |
Listele<T> | std::vector<T> 2 | std::vector<T> 3 | İçinde: &[T] 4 Dışarı: Vec<T> |
Dosya Tanımlayıcı | android::base::unique_fd | Yok | binder::parsel::ParselFileDescriptor |
Paket Dosyası Tanımlayıcısı | android::os::ParcelFileDescriptor | ndk::ScopedFileDescriptor | bağlayıcı::parsel::ParselFileDescriptor |
arayüz tipi (T) | android::sp<T> | std::shared_ptr<T> | ciltleyici::Güçlü |
parsellenebilir tip (T) | T | T | T |
birleştirme türü (T) 5 | T | T | T |
T[N] 6 | std::array<T, N> | std::array<T, N> | [T; N] |
1. Android 12 veya sonraki sürümlerde bayt dizileri uyumluluk nedeniyle int8_t yerine uint8_t kullanır.
2. C++ arka ucu List<T>
destekler; burada T
, String
, IBinder
, ParcelFileDescriptor
veya parsellenebilirlerden biridir. Android 13 veya sonraki sürümlerde T
, diziler dışında ilkel olmayan herhangi bir tür (arayüz türleri dahil) olabilir. AOSP, tüm arka uçlarda çalıştığı için T[]
gibi dizi türlerini kullanmanızı önerir.
3. NDK arka ucu List<T>
destekler; burada T
, String
, ParcelFileDescriptor
veya parsellenebilirlerden biridir. Android 13 veya sonraki sürümlerde T
, diziler dışında ilkel olmayan herhangi bir tür olabilir.
4. Türler, giriş (bağımsız değişken) veya çıktı (döndürülen değer) olmalarına bağlı olarak Rust kodu için farklı şekilde iletilir.
5. Birlik türleri Android 12 ve üzeri sürümlerde desteklenir.
6. Android 13 veya sonraki sürümlerde sabit boyutlu diziler desteklenir. Sabit boyutlu diziler birden fazla boyuta sahip olabilir (örn. int[3][4]
). Java arka ucunda sabit boyutlu diziler dizi türleri olarak temsil edilir.
Yönlülük (içeri/dışarı/içeriye)
İşlevlerin argüman türlerini belirlerken bunları in
, out
veya inout
olarak belirtebilirsiniz. Bu, bir IPC çağrısı için hangi yön bilgisinin iletildiğini kontrol eder. in
yöndür ve verinin arayan kişiden aranan kişiye iletildiğini gösterir. out
verinin aranan kişiden arayana iletildiği anlamına gelir. inout
her ikisinin birleşimidir. Ancak Android ekibi, inout
argüman belirtecini kullanmaktan kaçınmanızı önerir. Eğer inout
sürümlendirilmiş bir arayüz ve daha eski bir arayanla kullanırsanız, yalnızca arayanda bulunan ek alanlar varsayılan değerlerine sıfırlanır. Rust'a göre, normal bir inout
türü &mut Vec<T>
alır ve bir liste inout
türü &mut Vec<T>
alır.
UTF8/UTF16
CPP arka ucuyla dizelerin utf-8 mi yoksa utf-16 mı olacağını seçebilirsiniz. Dizeleri otomatik olarak utf-8'e dönüştürmek için AIDL'de @utf8InCpp String
olarak bildirin. NDK ve Rust arka uçları her zaman utf-8 dizelerini kullanır. utf8InCpp
ek açıklaması hakkında daha fazla bilgi için bkz. AIDL'deki Ek Açıklamalar .
İptal edilebilirlik
Boş değerleri CPP ve NDK arka uçlarına göstermek için Java arka ucunda null olabilen türlere @nullable
ile açıklama ekleyebilirsiniz. Rust arka ucunda bu @nullable
türleri Option<T>
olarak gösterilir. Yerel sunucular varsayılan olarak boş değerleri reddeder. Bunun tek istisnası, NDK okumaları ve CPP/NDK yazmaları için her zaman null olabilen interface
ve IBinder
türleridir. nullable
ek açıklama hakkında daha fazla bilgi için bkz. AIDL'deki Ek Açıklamalar .
Özel Parsellenebilirler
Özel parsellenebilir, hedef arka uçta manuel olarak uygulanan bir parsellenebilirdir. Özel parsellenebilirleri yalnızca, değiştirilemeyen mevcut bir özel parsellenebilir için diğer dillere destek eklemeye çalıştığınızda kullanın.
AIDL'nin bunu bilmesini sağlayacak şekilde özel bir parsellenebilir beyan etmek için, AIDL parsellenebilir beyanı şu şekilde görünür:
package my.pack.age;
parcelable Foo;
Varsayılan olarak bu, my.pack.age.Foo
Parcelable
arayüzünü uygulayan bir Java sınıfı olduğu bir Java ayrıştırılabilirini bildirir.
AIDL'de parsellenebilir özel bir CPP arka uç bildirimi için cpp_header
kullanın:
package my.pack.age;
parcelable Foo cpp_header "my/pack/age/Foo.h";
my/pack/age/Foo.h
dosyasındaki C++ uygulaması şuna benzer:
#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);
};
AIDL'de ayrıştırılabilir özel bir NDK bildirimi için ndk_header
kullanın:
package my.pack.age;
parcelable Foo ndk_header "android/pack/age/Foo.h";
android/pack/age/Foo.h
dosyasındaki NDK uygulaması şuna benzer:
#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);
};
Android 15'te (AOSP deneysel), AIDL'de özel bir Rust parsellenebilirinin bildirilmesi için rust_type
kullanın:
package my.pack.age;
@RustOnlyStableParcelable parcelable Foo rust_type "rust_crate::Foo";
rust_crate/src/lib.rs
dosyasındaki Rust uygulaması şuna benzer:
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);
Daha sonra bu ayrıştırılabiliri AIDL dosyalarında bir tür olarak kullanabilirsiniz, ancak AIDL tarafından oluşturulmayacaktır. union
içinde kullanmak üzere CPP/NDK arka uç özel parsellenebilirlerine yönelik <
ve ==
operatörlerini sağlayın.
Varsayılan değerler
Yapılandırılmış parsellenebilirler, temel öğeler, String
öğeleri ve bu türdeki diziler için alan başına varsayılan değerleri bildirebilir.
parcelable Foo {
int numField = 42;
String stringField = "string value";
char charValue = 'a';
...
}
Java arka ucunda, varsayılan değerler eksik olduğunda alanlar, ilkel türler için sıfır değerler ve ilkel olmayan türler için null
olarak başlatılır.
Diğer arka uçlarda, varsayılan değerler tanımlanmadığında alanlar varsayılan başlatılan değerlerle başlatılır. Örneğin, C++ arka ucunda, String
alanları boş bir dize olarak başlatılır ve List<T>
alanları boş bir vector<T>
olarak başlatılır. @nullable
alanlar, null değerli alanlar olarak başlatılır.
Hata yönetimi
Android işletim sistemi, hizmetlerin hataları bildirirken kullanması için yerleşik hata türleri sağlar. Bunlar ciltleyici tarafından kullanılır ve ciltleyici arayüzünü uygulayan herhangi bir hizmet tarafından kullanılabilir. Kullanımları AIDL tanımında iyi bir şekilde belgelenmiştir ve kullanıcı tanımlı herhangi bir durum veya dönüş türü gerektirmezler.
Hatalı çıkış parametreleri
Bir AIDL işlevi bir hata bildirdiğinde, işlev çıkış parametrelerini başlatamayabilir veya değiştiremeyebilir. Spesifik olarak, hata, işlemin kendisinin işlenmesi sırasında meydana gelmesinin aksine, ayrıştırma sırasında meydana gelirse, çıkış parametreleri değiştirilebilir. Genel olarak, bir AIDL işlevinden hata alındığında, tüm inout
ve out
parametrelerinin yanı sıra dönüş değerinin (bazı arka uçlarda out
parametresi gibi davranır) belirsiz bir durumda olduğu düşünülmelidir.
Hangi hata değerlerinin kullanılacağı
Yerleşik hata değerlerinin çoğu herhangi bir AIDL arabiriminde kullanılabilir ancak bazıları özel bir şekilde ele alınır. Örneğin, EX_UNSUPPORTED_OPERATION
ve EX_ILLEGAL_ARGUMENT
, hata durumunu açıklarken kullanılabilir ancak EX_TRANSACTION_FAILED
, temeldeki altyapı tarafından özel olarak ele alındığından kullanılmamalıdır. Bu yerleşik değerler hakkında daha fazla bilgi için arka uca özgü tanımları kontrol edin.
AIDL arayüzü, yerleşik hata türleri tarafından kapsanmayan ek hata değerleri gerektiriyorsa, kullanıcı tarafından tanımlanan hizmete özgü bir hata değerinin eklenmesine izin veren özel hizmete özgü yerleşik hatayı kullanabilir. . Bu hizmete özgü hatalar genellikle AIDL arayüzünde const int
veya int
destekli enum
olarak tanımlanır ve bağlayıcı tarafından ayrıştırılmaz.
Java'da hatalar, android.os.RemoteException
gibi istisnalarla eşleşir. Hizmete özgü istisnalar için Java, kullanıcı tanımlı hatayla birlikte android.os.ServiceSpecificException
öğesini kullanır.
Android'deki yerel kod istisnalar kullanmaz. CPP arka ucu android::binder::Status
kullanır. NDK arka ucu ndk::ScopedAStatus
kullanır. AIDL tarafından oluşturulan her yöntem, yöntemin durumunu temsil eden bunlardan birini döndürür. Rust arka ucu, NDK ile aynı istisna kodu değerlerini kullanır, ancak bunları kullanıcıya teslim etmeden önce bunları yerel Rust hatalarına ( StatusCode
, ExceptionCode
) dönüştürür. Hizmete özgü hatalar için, döndürülen Status
veya ScopedAStatus
kullanıcı tanımlı hatayla birlikte EX_SERVICE_SPECIFIC
kullanır.
Yerleşik hata türleri aşağıdaki dosyalarda bulunabilir:
Arka uç | Tanım |
---|---|
Java | android/os/Parcel.java |
CPP | binder/Status.h |
NDK | android/binder_status.h |
Pas | android/binder_status.h |
Çeşitli arka uçları kullanma
Bu talimatlar Android platform koduna özeldir. Bu örnekler tanımlanmış bir tür olan my.package.IFoo
kullanır. Rust arka ucunun nasıl kullanılacağına ilişkin talimatlar için Android Rust Desenleri sayfasındaki Rust AIDL örneğine bakın.
Türleri içe aktarma
Tanımlanan tür ister bir arayüz, ister parsellenebilir, ister birleşim olsun, onu Java'ya aktarabilirsiniz:
import my.package.IFoo;
Veya CPP arka ucunda:
#include <my/package/IFoo.h>
Veya NDK arka ucunda (ekstra aidl
ad alanına dikkat edin):
#include <aidl/my/package/IFoo.h>
Veya Rust arka ucunda:
use my_package::aidl::my::package::IFoo;
Java'da iç içe geçmiş bir türü içe aktarabilseniz de, CPP/NDK arka uçlarında kök türüne ilişkin başlığı eklemeniz gerekir. Örneğin, my/package/IFoo.aidl
tanımlanan iç içe geçmiş bir Bar
türünü içe aktarırken ( IFoo
dosyanın kök türüdür), CPP arka ucu için <my/package/IFoo.h>
(veya <aidl/my/package/IFoo.h>
eklemelisiniz NDK arka ucu için <aidl/my/package/IFoo.h>
).
Hizmetlerin uygulanması
Bir hizmeti uygulamak için yerel saplama sınıfından miras almanız gerekir. Bu sınıf, ciltleyici sürücüsünden komutları okur ve uyguladığınız yöntemleri yürütür. Bunun gibi bir AIDL dosyanız olduğunu hayal edin:
package my.package;
interface IFoo {
int doFoo();
}
Java'da bu sınıftan genişletme yapmanız gerekir:
import my.package.IFoo;
public class MyFoo extends IFoo.Stub {
@Override
int doFoo() { ... }
}
CPP arka ucunda:
#include <my/package/BnFoo.h>
class MyFoo : public my::package::BnFoo {
android::binder::Status doFoo(int32_t* out) override;
}
NDK arka ucunda (ekstra aidl
ad alanına dikkat edin):
#include <aidl/my/package/BnFoo.h>
class MyFoo : public aidl::my::package::BnFoo {
ndk::ScopedAStatus doFoo(int32_t* out) override;
}
Rust arka ucunda:
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(())
}
}
Veya eşzamansız Rust ile:
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(())
}
}
Kayıt olma ve hizmet alma
Android platformundaki hizmetler genellikle servicemanager
işlemine kaydedilir. Aşağıdaki API'lere ek olarak bazı API'ler hizmeti kontrol eder (yani hizmet mevcut değilse hemen geri dönerler). Kesin ayrıntılar için ilgili servicemanager
arayüzünü kontrol edin. Bu işlemler yalnızca Android platformuna karşı derleme yapılırken yapılabilir.
Java'da:
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"));
CPP arka ucunda:
#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"));
NDK arka ucunda (ekstra aidl
ad alanına dikkat edin):
#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")));
Rust arka ucunda:
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()
}
Tek iş parçacıklı çalışma zamanıyla eşzamansız Rust arka ucunda:
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
}
Diğer seçeneklerden önemli bir fark, async Rust ve tek iş parçacıklı çalışma zamanı kullanırken join_thread_pool
çağırmamamızdır . Bunun nedeni, Tokio'ya, ortaya çıkan görevleri yürütebileceği bir iş parçacığı vermeniz gerektiğidir. Bu örnekte ana iş parçacığı bu amaca hizmet edecektir. tokio::spawn
kullanılarak oluşturulan tüm görevler ana iş parçacığında yürütülecektir.
Çok iş parçacıklı bir çalışma zamanına sahip eşzamansız Rust arka ucunda:
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();
});
}
Çok iş parçacıklı Tokio çalışma zamanı ile oluşturulan görevler ana iş parçacığında yürütülmez. Bu nedenle, ana iş parçacığının yalnızca boşta kalmaması için ana iş parçacığında join_thread_pool
çağırmak daha mantıklı olur. Eşzamansız bağlamdan çıkmak için çağrıyı block_in_place
içine sarmanız gerekir.
Ölümle bağlantı kurmak
Ciltleyiciyi barındıran bir hizmet sona erdiğinde bildirim almayı isteyebilirsiniz. Bu, geri arama proxy'lerinin sızmasını önlemeye veya hata gidermeye yardımcı olabilir. Bu çağrıları ciltleyici proxy nesnelerinde yapın.
- Java'da
android.os.IBinder::linkToDeath
kullanın. - CPP arka ucunda
android::IBinder::linkToDeath
kullanın. - NDK arka ucunda
AIBinder_linkToDeath
kullanın. - Rust arka ucunda bir
DeathRecipient
nesnesi oluşturun ve ardındanmy_binder.link_to_death(&mut my_death_recipient)
çağırın. Geri çağırmanın sahibiDeathRecipient
olduğundan, bildirim almak istediğiniz sürece o nesneyi canlı tutmanız gerektiğini unutmayın.
Arayan bilgisi
Bir çekirdek bağlayıcı çağrısı alındığında, arayan bilgisi çeşitli API'lerde mevcuttur. PID (veya İşlem Kimliği), bir işlem gönderen işlemin Linux işlem kimliğini ifade eder. UID (veya Kullanıcı Kimliği), Linux kullanıcı kimliğini ifade eder. Tek yönlü bir çağrı alındığında, arayan PID 0'dır. Bir ciltleyici işlem bağlamı dışındayken, bu işlevler geçerli işlemin PID'sini ve UID'sini döndürür.
Java arka ucunda:
... = Binder.getCallingPid();
... = Binder.getCallingUid();
CPP arka ucunda:
... = IPCThreadState::self()->getCallingPid();
... = IPCThreadState::self()->getCallingUid();
NDK arka ucunda:
... = AIBinder_getCallingPid();
... = AIBinder_getCallingUid();
Rust arka ucunda, arayüzü uygularken aşağıdakileri belirtin (varsayılana izin vermek yerine):
... = ThreadState::get_calling_pid();
... = ThreadState::get_calling_uid();
Hizmetler için hata raporları ve hata ayıklama API'si
Hata raporları çalıştırıldığında (örneğin, adb bugreport
ile), çeşitli sorunların hata ayıklamasına yardımcı olmak için sistemin her yerinden bilgi toplarlar. AIDL hizmetleri için hata raporları, bilgilerini hata raporuna dökmek üzere hizmet yöneticisine kayıtlı tüm hizmetlerdeki ikili dumpsys
kullanır. Ayrıca dumpsys SERVICE [ARGS]
içeren bir hizmetten bilgi almak için komut satırında dumpsys
kullanabilirsiniz. C++ ve Java arka uçlarında, addService
işlevine ek argümanlar kullanarak hizmetlerin boşaltılma sırasını kontrol edebilirsiniz. Hata ayıklama sırasında bir hizmetin PID'sini almak için dumpsys --pid SERVICE
da kullanabilirsiniz.
Hizmetinize özel çıktı eklemek için, AIDL dosyasında tanımlanan diğer herhangi bir IPC yöntemini uyguladığınız gibi, sunucu nesnenizdeki dump
yöntemini geçersiz kılabilirsiniz. Bunu yaparken, boşaltmayı android.permission.DUMP
uygulama izniyle sınırlamanız veya boşaltmayı belirli UID'lerle kısıtlamanız gerekir.
Java arka ucunda:
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {...}
CPP arka ucunda:
status_t dump(int, const android::android::Vector<android::String16>&) override;
NDK arka ucunda:
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
Rust arka ucunda, arayüzü uygularken aşağıdakileri belirtin (varsayılana izin vermek yerine):
fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>
Dinamik olarak arayüz tanımlayıcısını alma
Arayüz tanımlayıcısı bir arayüzün tipini tanımlar. Bu, hata ayıklama sırasında veya bilinmeyen bir ciltleyiciniz olduğunda kullanışlıdır.
Java'da arayüz tanımlayıcısını aşağıdaki gibi kodlarla alabilirsiniz:
service = /* get ahold of service object */
... = service.asBinder().getInterfaceDescriptor();
CPP arka ucunda:
service = /* get ahold of service object */
... = IInterface::asBinder(service)->getInterfaceDescriptor();
NDK ve Rust arka uçları bu işlevselliği desteklemez.
Statik olarak arayüz tanımlayıcısını alma
Bazen (örneğin @VintfStability
hizmetlerini kaydederken), arayüz tanımlayıcısının statik olarak ne olduğunu bilmeniz gerekir. Java'da, aşağıdaki gibi bir kod ekleyerek tanımlayıcıyı alabilirsiniz:
import my.package.IFoo;
... IFoo.DESCRIPTOR
CPP arka ucunda:
#include <my/package/BnFoo.h>
... my::package::BnFoo::descriptor
NDK arka ucunda (ekstra aidl
ad alanına dikkat edin):
#include <aidl/my/package/BnFoo.h>
... aidl::my::package::BnFoo::descriptor
Rust arka ucunda:
aidl::my::package::BnFoo::get_descriptor()
Numaralandırma Aralığı
Yerel arka uçlarda, bir numaralandırmanın alabileceği olası değerleri yineleyebilirsiniz. Kod boyutuyla ilgili hususlar nedeniyle bu, şu anda Java'da desteklenmemektedir.
AIDL'de tanımlanan bir enum MyEnum
için yineleme aşağıdaki şekilde sağlanır.
CPP arka ucunda:
::android::enum_range<MyEnum>()
NDK arka ucunda:
::ndk::enum_range<MyEnum>()
Rust arka ucunda:
MyEnum::enum_values()
Konu yönetimi
Bir işlemdeki her libbinder
örneği bir iş parçacığı havuzunu korur. Çoğu kullanım durumunda bu, tüm arka uçlarda paylaşılan tam olarak tek bir iş parçacığı havuzu olmalıdır. Bunun tek istisnası, satıcı kodunun /dev/vndbinder
ile konuşmak için libbinder
başka bir kopyasını yükleyebileceği durumdur. Bu ayrı bir bağlayıcı düğümde olduğundan iş parçacığı havuzu paylaşılmaz.
Java arka ucu için iş parçacığı havuzunun yalnızca boyutu artabilir (zaten başlatıldığı için):
BinderInternal.setMaxThreads(<new larger value>);
CPP arka ucu için aşağıdaki işlemler mevcuttur:
// 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();
Benzer şekilde NDK arka ucunda:
bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
ABinderProcess_startThreadPool();
ABinderProcess_joinThreadPool();
Rust arka ucunda:
binder::ProcessState::start_thread_pool();
binder::add_service(“myservice”, my_service_binder).expect(“Failed to register service?”);
binder::ProcessState::join_thread_pool();
Eşzamansız Rust arka ucuyla iki iş parçacığı havuzuna ihtiyacınız vardır: binder ve Tokio. Bu, async Rust kullanan uygulamaların, özellikle join_thread_pool
kullanımı söz konusu olduğunda özel dikkat edilmesi gerektiği anlamına gelir. Bu konuda daha fazla bilgi için hizmetlerin kaydedilmesi bölümüne bakın.
Ayrılmış İsimler
C++, Java ve Rust, bazı adları anahtar sözcük olarak veya dile özgü kullanım için ayırır. AIDL, dil kurallarına dayalı kısıtlamaları uygulamasa da, ayrılmış bir adla eşleşen alan veya tür adlarının kullanılması, C++ veya Java için derleme hatasına neden olabilir. Rust için alan veya tür, r#
öneki kullanılarak erişilebilen "ham tanımlayıcı" sözdizimi kullanılarak yeniden adlandırılır.
Ergonomik olmayan bağlamaları veya doğrudan derleme hatasını önlemek için mümkün olduğu durumlarda AIDL tanımlarınızda ayrılmış adlar kullanmaktan kaçınmanızı öneririz.
AIDL tanımlarınızda zaten ayrılmış adlarınız varsa, protokol uyumlu kalarak alanları güvenle yeniden adlandırabilirsiniz; oluşturmaya devam etmek için kodunuzu güncellemeniz gerekebilir, ancak önceden oluşturulmuş programlar birlikte çalışmaya devam edecektir.
Kaçınılması gereken adlar: * C++ Anahtar Kelimeleri * Java Anahtar Kelimeleri * Rust Anahtar Kelimeleri