AIDL arka uçları

AIDL arka ucu, saplama kodu oluşturma hedefidir. AIDL dosyalarını kullanırken bunları her zaman belirli bir dilde ve belirli bir çalışma zamanında kullanırsınız. 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 ikili programından bağımsız olarak iletilebilecek şekilde bu API yüzeyinde derlenebilmesidir.

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

Arka Uç Dil API yüzeyi Bina sistemi
Java Java SDK/SystemApi (kararlı*) tümü
NDK C++ libbinder_ndk (kararlı*) 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 yönetimi gibi API'lerin çoğu dahili platform kullanımı için ayrılmıştır ve uygulamalar tarafından kullanılamaz. Uygulamalarda AIDL'yi kullanma hakkında daha fazla bilgi için geliştirici belgelerine bakın.
  • Rust arka ucu, Android 12'de kullanıma sunulmuştur. NDK arka ucu, Android 10 itibarıyla kullanıma sunulmuştur.
  • Paslı kasa, sabit ve taşınabilir olmasını sağlayan libbinder_ndk üzerine inşa edilmiştir. APEX'ler, bağlayıcı kasayı sistem tarafındaki diğer kişilerle aynı şekilde kullanır. Paslı kısım bir APEX'e paketlenmiş ve içinde gönderilir. Bu, sistem bölümündeki libbinder_ndk.so değerine bağlıdır.

Bina sistemi

Arka uca bağlı olarak AIDL'yi saplama kodu olarak derlemenin iki yolu vardır. Derleme sistemleri hakkında daha fazla bilgi için Soong Modülü Referansı'na bakın.

Çekirdek 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ı 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. Derleme 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ıldığını unutmayın. AIDL dosyalarının kaynak dosya olarak belirtilmediğinden rust_ modüller farklı şekilde işlenir. Bunun yerine, aidl_interface modülü bağlanabileceğiniz <aidl_interface name>-rust adlı bir rustlib üretir. Daha ayrıntılı bilgi için Rust AIDL örneğini inceleyin.

aidl_arayüz

Bu derleme sistemiyle kullanılan türler yapılandırılmış olmalıdır. Yapılandırılmış olmaları için parseller doğrudan alanları içermeli ve doğrudan hedef dillerde tanımlanan türlerin bildirimleri olmamalıdır. Yapılandırılmış AIDL'nin kararlı AIDL ile uyumu için Yapılandırılmış ve kararlı AIDL başlıklı makaleyi inceleyin.

Türler

aidl derleyicisini, türler için bir referans uygulaması olarak düşünebilirsiniz. Bir arayüz oluşturduğunuzda, sonuçta elde edilen arayüz dosyasını görmek için aidl --lang=<backend> ... yöntemini çağırın. aidl_interface modülünü kullandığınızda çıkışı out/soong/.intermediates/<path to module>/ üzerinde görüntüleyebilirsiniz.

Java/AIDL Türü C++ Türü NDK Türü Pas Türü
Boole Bool Bool Bool
bayt int8_t int8_t i8
karakter 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
Bağlayıcı android::IBinder ndk::SpAIBinder binder::SpIBinder
Tr[] std::vector<T> std::vector<T> Giriş: &[T]
Çıkış: Vec<T>
bayt[] std::vektör<uint8_t> std::vector<int8_t>1 In: &[u8]
Dışarıda: Vec<u8>
Liste<T> std::vector<T>2 std::vector<T>3 İçinde: &[T]4
Dışarıda: Vec<T>
Dosya Açıklayıcı android::base::benzersiz_fd Yok binder::parcel::ParcelFileDescriptor
ParcelFileAçıklayıcı android::os::ParcelFileAçıklayıcı 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::dizi<T, N> std::dizi<T, N> [T; K]

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

2. T değerinin String, IBinder, ParcelFileDescriptor veya ayrıştırılabilir öğe olduğu durumlarda C++ arka ucu List<T> özelliğini destekler. T, Android 13 veya sonraki sürümlerde diziler hariç herhangi bir primitif olmayan tür (arayüz türleri dahil) olabilir. AOSP, tüm arka uçlarda çalıştıkları için T[] gibi dizi türlerini kullanmanızı önerir.

3. NDK arka ucu, T değerinin String, ParcelFileDescriptor veya ayrıştırılabilir öğe olduğu durumlarda List<T> özelliğini destekler. T, Android 13 veya sonraki sürümlerde diziler hariç herhangi bir primitif olmayan tür olabilir.

4. Türler, giriş (bağımsız değişken) veya çıkış (döndürülen değer) olmalarına bağlı olarak Rust kodu için farklı şekilde iletilir.

5. Birleştirme türleri, Android 12 ve sonraki sürümlerde desteklenir.

6. Android 13 veya sonraki sürümlerde sabit boyutlu diziler desteklenir. Sabit boyutlu dizilerin birden çok boyutu olabilir (ör. int[3][4]). Java arka ucunda sabit boyutlu diziler, dizi türü olarak temsil edilir.

7. Bağlayıcı SharedRefBase nesnesini örneklendirmek için SharedRefBase::make\<My\>(... args ...) işlevini kullanın. Bu işlev, bağlayıcının başka bir işleme ait olması ihtimaline karşı dahili olarak da yönetilen bir std::shared_ptr\<T\> nesnesi oluşturur. Nesnenin başka şekillerde oluşturulması iki kat sahipliğe neden olur.

Yön (giriş/çıkış/çıkış)

İşlev için bağımsız değişken türlerini belirtirken bunları in, out veya inout olarak belirtebilirsiniz. Bu, IPC çağrısı için hangi yön bilgilerinin iletildiğini kontrol eder. in, varsayılan yöndür ve verilerin arayandan aranan kişiye iletildiğini belirtir. out, verilerin çağrı yapandan arayana aktarıldığı anlamına gelir. inout, bu ikisinin kombinasyonudur. Ancak Android ekibi, inout bağımsız değişken tanımlayıcısını kullanmaktan kaçınmanızı önerir. inout öğesini sürümlü bir arayüz ve daha eski bir çağrı yapan kişiyle kullanırsanız yalnızca çağrıda bulunan ek alanlar varsayılan değerlerine sıfırlanır. Rust ile ilgili olarak normal bir inout türü &mut Vec<T>, inout listesi türü ise &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

PBM arka ucuyla dizelerin utf-8 veya utf-16 olmasını seçebilirsiniz. Dizeleri otomatik olarak utf-8'e dönüştürmek için AIDL'de @utf8InCpp String olarak tanımlayın. NDK ve Rust arka uçları her zaman utf-8 dizelerini kullanır. utf8InCpp ek açıklaması hakkında daha fazla bilgi için AIDL'deki notlar bölümüne bakın.

Boş değer atanabilirliği

CPP ve NDK arka uçlarına boş değerler göstermek için Java arka ucunda boş olabilecek türlere @nullable ile ek 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 boş olabilen interface ve IBinder türleridir. nullable ek açıklaması hakkında daha fazla bilgi için AIDL'deki notlar bölümüne bakın.

Özel parseller

Özel ayrıştırılabilir, hedef arka uçta manuel olarak uygulanan bir pakettir. Özel ayrıştırıcıları yalnızca değiştirilemeyen mevcut bir özel paket için başka dillere destek eklemeye çalışırken kullanın.

AIDL'nin bundan haberdar olması için özel bir parsel beyan etmek istiyorsanız AIDL paketleme beyanı şu şekilde görünür:

    package my.pack.age;
    parcelable Foo;

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

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 ürünündeki 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 (AOSP deneysel) AIDL'de özel Rust paketi tanımlamak için rust_type kodunu 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);

Bu ayrıştırıcıyı AIDL dosyalarında bir tür olarak kullanabilirsiniz, ancak AIDL tarafından oluşturulmaz. union ürününde kullanmak üzere PBM/NDK arka uç özel paketleri için < ve == operatörlerini sağlayın.

Varsayılan değerler

Yapılandırılmış ayrıştırılabilirler; temel öğeler, String'ler ve bu türlerdeki diziler için alan başına varsayılan değerler bildirebilir.

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

Java arka ucunda varsayılan değerler eksik olduğunda alanlar temel türler için sıfır değerleri, primitif 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ılmış değerlerle başlatılır. Örneğin, C++ arka ucunda String alanları boş dize olarak başlatılır, List<T> alanları ise boş vector<T> olarak başlatılır. @nullable alanları, boş değer alanları olarak başlatılır.

Hata işleme

Android OS, hizmetlerin hataları bildirirken kullanılacak yerleşik hata türleri sağlar. Bunlar, bağlayıcı tarafından kullanılır ve bağlayıcı arayüzü uygulayan tüm hizmetler tarafından kullanılabilir. 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

AIDL işlevi hata bildirdiğinde, işlev çıkış parametrelerini başlatamayabilir veya değiştiremeyebilir. Daha ayrıntılı olarak belirtmek gerekirse, çıkış parametreleri, hatanın işlemin işlenmesi sırasında meydana gelmesi yerine ayrıştırma sırasında meydana gelmesi durumunda değiştirilebilir. Genel olarak, bir AIDL işlevinden hata alınırken tüm inout ve out parametreleri ile döndürülen değerin (bazı arka uçlarda out parametresi gibi davranır) süresiz durumda olduğu kabul edilmelidir.

Kullanılacak hata değerleri

Yerleşik hata değerlerinin çoğu tüm AIDL arayüzlerinde kullanılabilir ancak bazıları özel bir şekilde ele alınır. Örneğin, EX_UNSUPPORTED_OPERATION ve EX_ILLEGAL_ARGUMENT, hata durumunu tanımlarken kullanılabilir ancak temel altyapı tarafından özel işlendiği için EX_TRANSACTION_FAILED 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ürlerinin kapsamında olmayan 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, AIDL arayüzünde genellikle 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şlenir. Java, hizmete özgü istisnalar için kullanıcı tanımlı hatayla birlikte android.os.ServiceSpecificException öğesini kullanır.

Android'deki yerel kodda istisnalar kullanılmaz. PBM 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 bu değerleri kullanıcıya teslim etmeden önce 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 değerini kullanır.

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, my.package.IFoo tanımlı bir tür kullanılmaktadır. Rust arka ucunun nasıl kullanılacağıyla ilgili talimatlar için Android Rust Kalıpları sayfasındaki Rust AIDL örneğine bakın.

İçe aktarma türleri

Tanımlanan türün arayüz, parcelable veya birleşik olmasına bakılmaksızın, bunu Java'ya aktarabilirsiniz:

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ına kök türünün başlığını eklemeniz gerekir. Örneğin, my/package/IFoo.aidl politikasında tanımlanan (IFoo, dosyanın kök türüdür) iç içe yerleştirilmiş bir Bar türünü içe aktarırken PBM 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 saplama 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. Şu şekilde bir AIDL dosyanızın olduğunu düşünün:

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

Java'da şu sınıfı genişletmeniz 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(())
        }
    }

Alternatif olarak eşzamansız Rust sürümlerinde:

    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 hizmetleri alma

Android platformundaki hizmetler genellikle servicemanager işlemine kaydedilir. Aşağıdaki API'lere ek olarak, bazı API'ler hizmeti kontrol eder (yani hizmet kullanılamadığında hemen geri döner). Tam ayrıntılar için ilgili servicemanager arayüzünü kontrol edin. Bu işlemler 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, eşzamansız Rust ve tek iş parçacıklı bir çalışma zamanı kullanırken join_thread_pool araması yapmamamızdır. Bunun nedeni, Tokio'ya üretilen görevleri yürütebileceği bir iş parçacığı vermenizin gerekmesidir. Bu örnekte, ana iş parçacığı 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ı ç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ı kullanıldığında, oluşturulan görevler ana iş parçacığında yürütülmez. Bu nedenle, ana iş parçacığının boşta olmaması için ana iş parçacığında join_thread_pool çağrısı yapmak daha iyi olur. Eşzamansız bağlamdan çıkmak için çağrıyı block_in_place içinde sarmalamanız gerekir.

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

  • Java'da android.os.IBinder::linkToDeath ifadesini 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 my_binder.link_to_death(&mut my_death_recipient) yöntemini çağırın. Geri çağırmanın sahibi DeathRecipient olduğundan, bildirim almak istediğiniz sürece söz konusu nesneyi aktif durumda tutmanız gerekir.

Arayan bilgileri

Çekirdek bağlayıcı çağrısı alırken arayan bilgileri çeşitli API'lerde bulunur. PID (veya İşlem Kimliği), işlemi gönderen işlemin Linux işlem kimliğini ifade eder. UID (veya User-ID), Linux kullanıcı kimliğini ifade eder. Tek yönlü arama alırken çağrı PID'si 0'dır. Bağlayıcı işlem bağlamının 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();

PBM arka ucunda:

    ... = IPCThreadState::self()->getCallingPid();
    ... = IPCThreadState::self()->getCallingUid();

NDK arka ucunda:

    ... = AIBinder_getCallingPid();
    ... = AIBinder_getCallingUid();

Rust arka ucunda, arayüzü uygularken (varsayılan olarak izin vermek yerine) aşağıdakileri belirtin:

    ... = 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), çeşitli sorunların giderilmesine yardımcı olmak için sistemin her yerinden bilgiler toplar. AIDL hizmetleri için hata raporları, hizmet yöneticisine kayıtlı tüm hizmetlerde dumpsys ikili programını kullanarak bilgilerin hata raporuna dökümünü sağlar. dumpsys SERVICE [ARGS] ile bir hizmetten bilgi almak için komut satırında dumpsys kullanabilirsiniz. C++ ve Java arka uçlarında, addService işlevine ek bağımsız değişkenler kullanarak hizmetlerin dökümü alınma sırasını kontrol edebilirsiniz. Hata ayıklama sırasında bir hizmetin PID'sini almak için dumpsys --pid SERVICE öğesini de kullanabilirsiniz.

Hizmetinize özel çıkış eklemek için AIDL dosyasında tanımlanan başka bir IPC yöntemini uyguladığınız gibi sunucu nesnenizde dump yöntemini geçersiz kılabilirsiniz. Bunu yaparken, döküm işlemini android.permission.DUMP uygulama izniyle 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 olarak 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. Bu, hata ayıklama sırasında veya bilinmeyen bir bağlayıcınız olduğunda kullanışlıdır.

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 açıklayıcısını statik olarak al

Bazen (ör. @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 açıklayı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ığı

Yerel arka uçlarda, bir enum'un alabileceği olası değerleri yineleyebilirsiniz. Bu işlem, kod boyutuyla ilgili nedenlerden dolayı Java'da desteklenmemektedir.

AIDL'de tanımlanan bir sıralama 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ığı havuzuna sahiptir. Çoğu kullanım durumu için bu, tüm arka uçlar arasında paylaşılan tam olarak bir iş parçacığı havuzu olmalıdır. Bunun tek istisnası, tedarikçi kodunun /dev/vndbinder ile konuşmak için başka bir libbinder kopyasını yükleyebilmesidir. Bu, ayrı bir bağlayıcı düğümünde olduğundan iş parçacığı havuzu paylaşılmaz.

Java arka ucu için iş parçacığı havuzunun boyutu (zaten başlatıldığı için) yalnızca artabilir:

    BinderInternal.setMaxThreads(<new larger value>);

PBM 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 nedenle, eşzamansız Rust kullanan uygulamaların özellikle join_thread_pool kullanımı söz konusu olduğunda özel noktalara dikkat etmesi gerekir. Bu konuda daha fazla bilgi edinmek için hizmet kaydetme bölümüne göz atın.

Ayrılmış adlar

C++, Java ve Rust bazı adları anahtar kelime olarak veya dile özel kullanım için ayırıyor. AIDL dil kurallarına göre 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# önekiyle erişilebilen "ham tanımlayıcı" söz dizimi kullanılarak yeniden adlandırılır.

Ergonomik olmayan bağlamaları veya açık derleme hatasını önlemek için mümkün olduğunda 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 kalırken alanları güvenle yeniden adlandırabilirsiniz. Geliştirmeye devam etmek için kodunuzu güncellemeniz gerekebilir ancak önceden oluşturulmuş programlar birlikte çalışmaya devam edecektir.

Kaçınılması gereken adlar: