AIDL arka uçları

AIDL arka ucu, saplama kodu oluşturma hedefidir. AIDL dosyalarını kullanırken her zaman belirli bir çalışma zamanında belirli bir dilde kullanın. Bağlama bağlı olarak farklı AIDL arka uçları kullanmanız gerekir.

Aşağıdaki tabloda API yüzeyinin kararlılığı, kodun system.img libbinder.so ikilisinden bağımsız olarak yayınlanabileceği şekilde bu API yüzeyine göre derlenebilme özelliğini ifade eder.

AIDL aşağıdaki arka uçlara sahiptir:

Arka Uç Dil API yüzeyi Derleme sistemleri
Java Java SDK/SystemApi (stabil*) tümü
NDK C++ libbinder_ndk (stable*) aidl_arayüz
ABM C++ libbinder (kararsız) tümü
Rust Rust libbinder_rs (kararlı*) aidl_arayüz
  • Bu API yüzeyleri sabittir ancak hizmet için olanlar gibi API'lerin çoğu kullanılabilir. dahili platform kullanımına ayrılmıştır ve kullanıcılar tarafından 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 kullanıma sunuldu. "the" NDK arka ucu, Android 10 itibarıyla kullanıma sunulmuştur.
  • Paslı kasa, libbinder_ndk adlı binanın üzerine inşa edilmiştir. Bu da sabit ve taşınabilir olması gerekir. APEX'ler, bağlayıcı paketini sistem tarafındaki diğer kullanıcılarla aynı şekilde kullanır. Paslı kısım bir APEX'e paket halinde sunulur ve inceleyeceğiz. Bu, sistem bölümündeki libbinder_ndk.so değerine bağlıdır.

Bina sistemi

Arka uça bağlı olarak, AIDL'yi stub koduna derlemenin iki yolu vardır. Derleme sistemleri hakkında daha fazla bilgi için Soong Modülü Referansı başlıklı makaleyi inceleyin.

Temel derleme sistemi

Herhangi bir cc_ veya java_ Android.bp modülünde (veya Android.mk eşdeğerlerinde) .aidl dosyaları kaynak dosya olarak belirtilebilir. Bu durumda, AIDL'nin Java/CPP arka uçları (NDK arka ucu değil) kullanılır ve ilgili AIDL dosyalarını kullanacak sınıflar modüle otomatik olarak eklenir. Seçenekler Örneğin local_include_dirs, derleme sistemine kök yolunu Söz konusu modüldeki AIDL dosyaları bu modüllerde aidl: grubu. Rust arka ucunun yalnızca Rust ile kullanılabileceğini unutmayın. rust_ modül farklı şekilde işlenir. Bunun yerine, aidl_interface modül rustlib Bağlantı kurulabilecek <aidl_interface name>-rust. Daha fazla bilgi için bkz. Rust AIDL örneği.

aidl_arayüz

Bu derleme sistemiyle kullanılan türler yapılandırılmış olmalıdır. Yapılandırılmış olması için parseller doğrudan alanları içermeli ve tür bildirimleri olmamalıdır hedef dillerde tanımlandığını gösterir. Yapılandırılmış AIDL'nin AIDL'nin kararlı çalışması için Yapılandırılmış ve kararlı AIDL başlıklı makaleyi inceleyin.

Türler

aidl derleyicisini türler için referans bir uygulama olarak düşünebilirsiniz. Arayüz oluşturduğunuzda aidl --lang=<backend> ... komutunu çağırarak dosya oluşturma işlemi dahildir. aidl_interface modülünü kullandığınızda şunları görüntüleyebilirsiniz: out/soong/.intermediates/<path to module>/ çıktı.

Java/AIDL Türü C++ Türü NDK Türü Pas Türü
Boole Bool Bool bool
bayt int8_t int8_t i8
char karakter16_t karakter16_t u16
int tam32_t tam32_t i32
uzun int64_t int64_t i64
kayan kayan kayan "f32"
çift çift çift f64
Dize android::Dize16 std::string Dize
android.os.Parcelable android::Parcelable Yok Yok
IBinder android::IBinder ndk::SpAIBinder binder::SpIBinder
T[] std::vector<T> std::vector<T> Giriş: &[T]
Çıkış: Vec<T>
byte[] std::vector<uint8_t> std::vector<int8_t>1 Giriş: &[u8]
Çıkış: Vec<u8>
Liste<T> std::vector<T>2 std::vector<T>3 Giriş: &[T]4
Çıkış: Vec<T>
Dosya Açıklayıcı android::base::benzersiz_fd Yok binder::parcel::ParcelFileDescriptor
ParcelFileAçıklayıcı android::os::ParcelFileDescriptor ndk::ScopedFileDescriptor binder::parcel::ParcelFileDescriptor
arayüz türü (T) android::sp<T> std::shared_ptr<T>7 binder::Güçlü
ayrıştırılabilir türü (T) T T T
birlik türü (T)5 T T T
T[N] 6 std::array<T, N> std::dizi<T, N> [T; N]

1. Android 12 veya sonraki sürümlerde, uyumluluk nedeniyle bayt dizileri int8_t yerine uint8_t kullanır.

2. C++ arka ucu List<T> değerini destekler. T, String değerlerinden biridir. IBinder, ParcelFileDescriptor veya ayrıştırılabilir. Android 13 veya sonraki sürümlerde T, diziler dışında primitif olmayan herhangi bir tür (arayüz türleri dahil) olabilir. AOSP, Tüm arka uçlarda çalıştıkları için T[] gibi dizi türlerini kullanmalıdır. 'nı inceleyin.

3. NDK arka ucu, T String, ParcelFileDescriptor veya parcelable değerlerinden biri olduğunda List<T> değerini destekler. Android 13'te veya daha yüksek değerler T, diziler hariç herhangi bir primitif olmayan tür olabilir. 'nı inceleyin.

4. Rust kodu için türler, giriş (bağımsız değişken) veya çıkış (döndürülen değer) olabilir.

5. Birlik türleri Android 12 ve sonraki sürümlerde desteklenir daha yüksek olabilir.

6. Android 13 veya sonraki sürümlerde sabit boyutlu diziler desteklenir. Sabit boyutlu diziler birden fazla boyuta sahip olabilir (ör. int[3][4]). Java arka ucunda sabit boyutlu diziler dizi türleri olarak temsil edilir.

7. SharedRefBase bağlayıcı nesnesini örneklendirmek için şunu kullanın: SharedRefBase::make\<My\>(... args ...). Bu işlev, bir std::shared_ptr\<T\> nesnesi oluşturur. Bu nesne, bağlayıcının başka bir sürece ait olması durumunda da dahili olarak yönetilir. Nesnenin başka yollarla oluşturulması çift sahipliğe neden olur.

Yön (in/out/inout)

Fonksiyonların bağımsız değişkenlerini belirtirken, Bunları in, out veya inout olarak düzenleyebilirsiniz. Bu, bir IPC çağrısı için bilgilerin hangi yönde iletileceğini kontrol eder. in, varsayılan yöndür ve verilerin doğru yönde arayandan aranan kişiye aktarılır. out, verilerin ona göstermeniz gerekir. inout, bu ikisinin kombinasyonudur. Ancak Android Ekibi, bağımsız değişken belirteci inout kullanmamanızı önerir. inout'ü sürümlü bir arayüzle ve eski bir arananla kullanıyorsanız yalnızca arayanda bulunan ek alanlar varsayılan değerlerine sıfırlanır. Rust ile ilgili olarak normal bir inout türü &mut Vec<T> alır ve inout türündeki bir liste &mut Vec<T> alır.

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

CPP arka ucu ile dizelerin utf-8 mi yoksa utf-16 mı olacağını seçebilirsiniz. Bildir dizeleri otomatik olarak utf-8'e dönüştürmek için AIDL'de @utf8InCpp String olarak kullanın. NDK ve Rust arka uçları her zaman utf-8 dizelerini kullanır. Daha fazla bilgi için utf8InCpp ek açıklaması için AIDL'deki ek açıklamalar bölümüne bakın.

Boş değer atanabilirliği

Boş olabilecek türleri @nullable ile ek açıklamayla belirtebilirsiniz. nullable ek açıklaması hakkında daha fazla bilgi için bkz. AIDL'deki ek açıklamalar.

Özel kargolanabilir ürünler

Özel paketlenebilir, hedef arka uçta manuel olarak uygulanan bir paketlenebilir öğedir. Özel ayrıştırıcıları yalnızca diğer ürünlere destek eklemeye çalışırken veya değiştirilemeyen mevcut bir özel ayrıştırıcının dilleridir.

AIDL'nin, özel bir parsel hakkında bilgi sahibi olması için ayrıştırılabilir beyannamesi aşağıdaki gibi görünür:

    package my.pack.age;
    parcelable Foo;

Varsayılan olarak bu, my.pack.age.Foo değerinin bir Java olduğu bir Java paketi bildirir Parcelable arayüzünü uygulayan sınıftır.

AIDL'de ayrıştırılabilir özel PBM arka uç beyanı için cpp_header kullanın:

    package my.pack.age;
    parcelable Foo cpp_header "my/pack/age/Foo.h";

my/pack/age/Foo.h'teki C++ uygulaması şu şekilde görünür:

    #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 özel NDK parsel beyanı için ndk_header kullanın:

    package my.pack.age;
    parcelable Foo ndk_header "android/pack/age/Foo.h";

android/pack/age/Foo.h içindeki NDK uygulaması şöyle görünür:

    #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, AIDL'de özel bir Rust paketlenebilir öğesini beyan etmek için rust_type kullanın:

package my.pack.age;
@RustOnlyStableParcelable parcelable Foo rust_type "rust_crate::Foo";

rust_crate/src/lib.rs için Rust uygulaması aşağıdaki gibi görünür:

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);

Ardından bu parcelable'ı AIDL dosyalarında tür olarak kullanabilirsiniz ancak AIDL tarafından oluşturulmaz. PPP/NDK arka ucu için < ve == operatörleri sağlayın union içinde kullanmak üzere özel parseller gönderebilirsiniz.

Varsayılan değerler

Yapılandırılmış paketlenebilirler, ilkel türler, String'ler ve bu türlerin dizileri için alan başına varsayılan değerler tanımlayabilir.

    parcelable Foo {
      int numField = 42;
      String stringField = "string value";
      char charValue = 'a';
      ...
    }

Java arka ucunda varsayılan değerler eksik olduğunda alanlar şu şekilde başlatılır: temel türler için sıfır, primitif olmayan türler için null değerleri.

Diğer arka uçlarda, varsayılan değerler tanımlanmadığında alanlar varsayılan olarak başlatılmış değerlerle başlatılır. Örneğin, C++ arka ucunda String alanları boş bir dize olarak, List<T> alanları ise boş bir vector<T> olarak başlatılır. @nullable alanları, boş değer alanları olarak başlatılır.

Hata işleme

Android OS, hizmetlerin raporlama sırasında kullanılacak yerleşik hata türleri sağlar hatalar. Bunlar bağlayıcı tarafından kullanılır ve bir bağlayıcı arayüzüdür. Kullanımları AIDL tanımında açıkça belirtilmiştir ve kullanıcı tanımlı bir durum veya dönüş türü gerektirmez.

Hatalı çıkış parametreleri

Bir AIDL işlevi hata bildirdiğinde işlev, çıkış parametrelerini başlatamaz veya değiştiremez. Özellikle, hata işlemin işlenmesi sırasında değil de paketin açılması sırasında meydana gelirse çıkış parametreleri değiştirilebilir. Genel olarak, AIDL'den hata alınırken fonksiyonunun yanı sıra tüm inout ve out parametrelerinin yanı sıra döndürülen değeri (yani, bazı arka uçlarda out parametresi gibi davranır) normal koşullarda, süresizdir.

Kullanılacak hata değerleri

Yerleşik hata değerlerinin çoğu tüm AIDL arayüzlerinde kullanılabilir, ancak özel bir şekilde ele alınıyor. Örneğin, hata durumunu tanımlarken EX_UNSUPPORTED_OPERATION ve EX_ILLEGAL_ARGUMENT kullanılmasında sakınca yoktur ancak temel altyapı tarafından özel olarak ele alındığı için EX_TRANSACTION_FAILED kullanılmamalıdır. Bu yerleşik değerler hakkında daha fazla bilgi için arka uçla ilgili 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 özel bir hata değerinin dahil edilmesine olanak tanıyan hizmete özel özel yerleşik hatayı kullanabilir. Bu hizmete özgü hatalar genellikle AIDL arayüzü const int veya int destekli enum olarak ve ayrıştırılmamış bağlayıcı.

Java'da hatalar, android.os.RemoteException gibi istisnalarla eşlenir. Örneğin, hizmete özgü istisnalar, Java, android.os.ServiceSpecificException kullanıcı tanımlı hatayla birlikte gösterilir.

Android'deki yerel kodda istisnalar kullanılmaz. SBM arka ucu android::binder::Status kullanır. NDK arka ucu ndk::ScopedAStatus kullanıyor. Hepsini yöntemi, AIDL tarafından oluşturulan bu yapılandırmanın durumunu temsil eden yöntemidir. Rust arka ucu, NDK ile aynı istisna kodu değerlerini kullanır ancak bunları kullanıcıya sunmadan önce yerel Rust hatalarına (StatusCode, ExceptionCode) dönüştürür. Hizmete özgü hatalar için, Status veya ScopedAStatus, EX_SERVICE_SPECIFIC ile birlikte kullanıcı tanımlı hata.

Yerleşik hata türleri aşağıdaki dosyalarda bulunabilir:

Arka uç Tanım
Java android/os/Parcel.java
ABM binder/Status.h
NDK android/binder_status.h
Rust android/binder_status.h

Çeşitli arka uçları kullanma

Bu talimatlar Android platform koduna özeldir. Bu örneklerde tanımlanmış tür, my.package.IFoo. Rust arka ucunun nasıl kullanılacağıyla ilgili talimatlar için Android Rust Desenleri sayfasındaki Rust AIDL örneğine bakın.

İçe aktarma türleri

Tanımlanan türün arayüz, ayrıştırılabilir veya birleşik olması fark etmeksizin tüm aynısını kullanın:

import my.package.IFoo;

Veya PBM arka ucunda:

#include <my/package/IFoo.h>

Alternatif olarak, NDK arka ucunda (ek 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 yerleştirilmiş bir türü içe aktarabilirsiniz ancak CPP/NDK arka uçlarında kök türünün üstbilgisini eklemeniz gerekir. Örneğin, my/package/IFoo.aidl içinde tanımlanan iç içe yerleştirilmiş 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 NDK arka ucu için <aidl/my/package/IFoo.h>) eklemeniz gerekir.

Hizmetleri uygulama

Bir hizmeti uygulamak için yerel stub sınıfından devralmanız gerekir. Bu sınıf, bağlayıcı sürücüsünden gelen komutları okur ve uyguladığınız yöntemleri yürütür. Aşağıdaki gibi bir AIDL dosyanız olduğunu varsayalım:

    package my.package;
    interface IFoo {
        int doFoo();
    }

Java'da şu sınıftan türetmeniz gerekir:

    import my.package.IFoo;
    public class MyFoo extends IFoo.Stub {
        @Override
        int doFoo() { ... }
    }

PBM arka ucunda:

    #include <my/package/BnFoo.h>
    class MyFoo : public my::package::BnFoo {
        android::binder::Status doFoo(int32_t* out) override;
    }

NDK arka ucunda (ek 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(())
        }
    }

Eşzamansız Rust ile de:

    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(())
        }
    }

Kaydolma ve hizmet alma

Android platformundaki hizmetler genellikle servicemanager işlemiyle kaydedilir. Aşağıdaki API'lere ek olarak, bazı API'ler hizmet kullanılamıyorsa (hizmet kullanılamıyorsa anında geri dönerler). Tam ayrıntılar için ilgili servicemanager arayüzünü kontrol edin. Bu işlemleri yalnızca Android platformuna göre derleme yaparken 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"));

PBM 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 (ek 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ı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 = "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çenekler arasındaki önemli farklardan biri, önceden aramamızı yapmadığımız join_thread_pool eş zamansız Rust ve tek iş parçacıklı bir çalışma zamanı kullanırken. Bunun nedeni, Tokio'ya oluşturulan görevleri yürütebileceği bir iş parçacığı vermeniz gerektiğidir. Bu örnekte ana ileti dizisi bu amaca hizmet eder. tokio::spawn kullanılarak oluşturulan tüm görevler ana iş parçacığında yürütülür.

Çok iş parçacıklı bir çalışma zamanında, 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ında, oluşturulan görevler ana iş parçacığında yürütülmez. Bu nedenle, ana makinede join_thread_pool işlevini çağırmak daha mantıklıdır. ana iş parçacığının sadece boşta olmamasını sağlayın. Görüşmeyi Eş zamansız bağlamı bırakmak için block_in_place.

Bağlayıcı barındıran bir hizmet öldüğünde bildirim alma isteğinde bulunabilirsiniz. Bu, geri çağırma proxy'lerinin sızmasını önlemeye yardımcı olabilir veya hata kurtarmaya yardımcı olabilir. Bu çağrıları bağlayıcı proxy nesnelerinde yapın.

  • Java'da android.os.IBinder::linkToDeath kullanın.
  • PBM arka ucunda android::IBinder::linkToDeath kullanın.
  • NDK arka ucunda AIBinder_linkToDeath kullanın.
  • Rust arka ucunda bir DeathRecipient nesnesi oluşturun ve ardından my_binder.link_to_death(&mut my_death_recipient) işlevini çağırın. Geri çağırma işlevinin sahibi DeathRecipient olduğundan, bildirim almak istediğiniz sürece bu nesneyi canlı tutmanız gerektiğini unutmayın.

Arayan bilgileri

Bir çekirdek bağlayıcı çağrısı alındığında arayan bilgileri çeşitli API'lerde kullanılabilir. PID (veya İşlem Kimliği), dosyanın Linux işlem kimliğini işlem gönderen bir işlemdir. UID (veya User-ID), Linux kullanıcı kimliği. Tek yönlü arama alırken çağrı PID'si 0'dır. Bu işlevler, bir bağlayıcı işlem bağlamının dışındayken geçerli işlemin PID ve UID değerini döndürür.

Java arka ucunda:

    ... = Binder.getCallingPid();
    ... = Binder.getCallingUid();

PBM 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ılan olarak ayarlamak yerine):

    ... = ThreadState::get_calling_pid();
    ... = ThreadState::get_calling_uid();

Hizmetler için hata raporları ve hata ayıklama API'si

Hata raporları çalıştığında (örneğin, adb bugreport ile) bilgi edinerek çeşitli sorunları gidermenize yardımcı olabilir. AIDL hizmetleri için hata raporları, tüm hizmetlerde dumpsys ikili programını kullanır hizmet yöneticisine kaydettirerek bilgilerini hata raporu oluşturun. Bilgi almak için komut satırında dumpsys tuşunu da kullanabilirsiniz dumpsys SERVICE [ARGS] ile bir hizmetten. C++ ve Java arka uçlarında, addService için ek bağımsız değişkenler kullanarak hizmetlerin döküldüğü sırayı kontrol edebilirsiniz. Bir cihazın PID'sini almak için dumpsys --pid SERVICE kullanabilirsiniz. hizmetten bahsetmek istiyorum.

Hizmetinize özel çıkış eklemek için sunucu nesnenizdeki dump yöntemini, bir AIDL dosyasında tanımlanan diğer IPC yöntemlerini uygular gibi geçersiz kılabilirsiniz. Bunu yaparken, döküm işlemini uygulama izni android.permission.DUMP ile veya belirli UID'lerle kısıtlamanız gerekir.

Java arka ucunda:

    @Override
    protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
        @Nullable String[] args) {...}

PBM 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 varsayılan ayarlara izin vermek yerine aşağıdakileri belirtin:

    fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>

Arayüz açıklayıcıyı dinamik olarak al

Arayüz tanımlayıcısı, bir arayüzün türünü tanımlar. Faydalı sırasında hata ayıklama sırasında veya bilinmeyen bir bağlayıcı olduğunda görebilirsiniz.

Java'da, arayüz tanımlayıcısını aşağıdaki gibi bir kodla alabilirsiniz:

    service = /* get ahold of service object */
    ... = service.asBinder().getInterfaceDescriptor();

PBM arka ucunda:

    service = /* get ahold of service object */
    ... = IInterface::asBinder(service)->getInterfaceDescriptor();

NDK ve Rust arka uçları bu özelliği desteklemez.

Arayüz tanımlayıcısını statik olarak alma

Bazen (ör. @VintfStability hizmetlerini kaydettirirken) arayüz tanımlayıcısının statik olarak ne olduğunu bilmek önemlidir. Java'da, aşağıdaki gibi bir kod ekleyerek tanımlayıcıyı alabilirsiniz:

    import my.package.IFoo;
    ... IFoo.DESCRIPTOR

PBM arka ucunda:

    #include <my/package/BnFoo.h>
    ... my::package::BnFoo::descriptor

NDK arka ucunda (ek 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()

Sıralama aralığı

Yerleşik arka uçlarda, bir enum'un alabileceği olası değerler üzerinde iterasyon yapabilirsiniz. Kod boyutu nedeniyle bu özellik Java'da desteklenmez.

AIDL'de tanımlanan bir enum MyEnum için yineleme aşağıdaki gibi sağlanır.

PBM arka ucunda:

    ::android::enum_range<MyEnum>()

NDK arka ucunda:

   ::ndk::enum_range<MyEnum>()

Rust arka ucunda:

    MyEnum::enum_values()

İleti dizisi yönetimi

Bir işlemdeki her libbinder örneği bir iş parçacığı havuzu bulundurur. Çoğu kullanım alanında bu, tüm arka uçlarda paylaşılan tam olarak bir iş parçacığı havuzu olmalıdır. Tek istisna, tedarikçi kodunun libbinder dokümanının başka bir kopyasını yükleyebileceği durumlardır /dev/vndbinder ile konuşmak için. Bu, ayrı bir bağlayıcı düğümde olduğu için ileti dizisi paylaşılmıyor.

Java arka ucu için iş parçacığının boyutu yalnızca zaten başladı):

    BinderInternal.setMaxThreads(<new larger value>);

CPP arka ucu için aşağıdaki işlemler kullanılabilir:

    // 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 ucunda iki iş parçacığı (iş parçacığı) kullanmanız gerekir: bağlayıcı ve Tokio. Bu, özellikle join_thread_pool kullanımı söz konusu olduğunda, asenkron Rust kullanan uygulamaların özel dikkat gerektirdiği anlamına gelir. Bu konu hakkında daha fazla bilgi hizmet kaydetme başlıklı makaleye göz atın.

Ayrılmış adlar

C++, Java ve Rust, bazı adları anahtar kelime 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ı kullanmak C++ veya Java için derleme hatasına neden olabilir. Rust için alan veya tür, r# ön eki kullanılarak erişilebilen "ham tanımlayıcı" söz dizimi kullanılarak yeniden adlandırılır.

Ergonomik olmayan bağlamaları veya derleme hatalarını önlemek için mümkün olduğunda AIDL tanımlarınıza ayrılmış adlar eklemekten kaçının.

AIDL tanımlarınızdaki ayrılmış adlar varsa protokol uyumluluğunu korurken alanları güvenle yeniden adlandırabilirsiniz. Derlemeye devam etmek için kodunuzu güncellemeniz gerekebilir ancak derlenmiş programlar birlikte çalışmaya devam eder.

Kaçınılması gereken adlar: