AIDL Arka Uçları

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.

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++ libbinder_ndk (kararlı*) aidl_interface
CPP C++ libbinder (kararsız) Tümü
Pas Pas libinder_rs (kararsız) aidl_interface
  • 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. 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ündeki libbinder_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::ParcelFileDescriptor
Paket Dosyası Tanımlayıcısı android::os::ParcelFileDescriptor ndk::ScopedFileDescriptor binder::parsel::ParcelFileDescriptor
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 parsellenebilirden 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 üzeri 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. varsayılan in ve verinin arayan kişiden aranan kişiye iletildiğini gösterir. out , verinin aranan kişiden arayana iletildiği anlamına gelir. inout bunların 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 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 bilmesi amacıyla ö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 beyanı 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, ilkel öğ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 değerler 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ı çıktı 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 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 ayrıştırılabilir, 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 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 dosyasında 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 gelen 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ülür.

Ç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.

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ından my_binder.link_to_death(&mut my_death_recipient) çağırın. DeathRecipient geri aramanın sahibi 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çin 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 herhangi bir IPC yöntemini uyguluyormuşsunuz 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, eşzamansız 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ğlamalardan veya doğrudan derleme hatasından kaçınmak 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