AIDL Arka Uçları

AIDL arka ucu, saplama kod oluşturma hedefidir. AIDL dosyalarını kullanırken bu dosyaları her zaman belirli bir çalışma zamanında belirli bir dilde kullanırsınız. Bağlama göre farklı AIDL arka uçları kullanmanız gerekir.

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

Arka uç Dil API yüzeyi Derleme sistemleri
Java Java SDK/SystemApi (kararlı*) tümü
Danimarka kronu C++ libbinder_ndk (kararlı*) aidl_interface
ABM C++ libbinder (kararlı değil) tümü
Paslı Paslı libbinder_rs (kararlı değil) aidl_interface
  • Bu API yüzeyleri kararlıtır, 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'nin nasıl kullanılacağı hakkında daha fazla bilgi için geliştirici belgelerine bakın.
  • Paslı arka uç, Android 12'de kullanıma sunuldu. NDK arka ucu, Android 10 itibarıyla kullanıma sunulmuştur.
  • Paslı kafes, libbinder_ndk üzerinde inşa edilmiştir. APEX'ler, bağlayıcı bölmesini sistem tarafındaki diğer kişilerle aynı şekilde kullanır. Paslı kısım bir APEX içinde paketlenir ve içine gönderilir. Bu, sistem bölümündeki libbinder_ndk.so öğesine bağlıdır.

Derleme sistemleri

Arka uca bağlı olarak AIDL'yi saplama koduna dönüştürmenin 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 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ı kullanan sınıflar modüle otomatik olarak eklenir. Derleme sistemine bu 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 birlikte kullanılacağını unutmayın. AIDL dosyalarının kaynak dosya olarak belirtilmediğinden rust_ modülleri farklı şekilde işlenir. Bunun yerine, aidl_interface modülü, bağlantı oluşturulabilen <aidl_interface name>-rust adında bir rustlib oluşturur. Daha ayrıntılı bilgi için Rust AIDL örneğine bakın.

aidl_interface

Kararlı AIDL sayfasına göz atın. Bu derleme sistemiyle kullanılan türler yapılandırılmalıdır; Bu durum doğrudan AIDL ile ifade edilir. Bu, özel ayrıştırılabilir öğelerin kullanılamayacağı anlamına gelir.

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, çıkışı out/soong/.intermediates/<path to module>/ içinde görüntüleyebilirsiniz.

Java/AIDL Türü C++ Türü NDK Türü Paslanma Türü
boole bool bool bool
bayt int8_t int8_t i8
karakter kt16_t kt16_t 16
int int32_t int32_t i32
uzun int64_t int64_t i64
float float float f32
double double double f64
Dize android::Dize16 std::dize Dize
android.os.Ayrıştırılabilir android::Ayrıştırılabilir Yok Yok
Bağlayıcı android::IBinder ndk::SpAIBinder binder::SpIBinder
T[] std::vektör<T> std::vektör<T> In: &T
Dışarı: Vec<T>
bayt[] std::vector<uint8_t> std::vector<int8_t>1 In: &[u8]
Dışarıda: Vec<u8>
Liste<T> std::vector<T>2 std::vector<T>3 In: &[T]4
Out: Vec<T>
Dosya Açıklayıcı android::base::unique_fd Yok binder::parsel::ParcelFileDescriptor
ParselDosya Tanımlayıcısı android::os::ParcelFileDescriptor ndk::ScopedFileDescriptor binder::parsel::ParcelFileDescriptor
arayüz türü (T) android::sp<T> std::shared_ptr<T> binder::Güçlü
ayrıştırılabilir tür (T) T T T
birlik türü (T)5 T T T

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

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

3. NDK arka ucu, T değerinin String, ParcelFileDescriptor veya ayrıştırılabilir değerlerden biri olduğu List<T> öğesini destekler. Android T (AOSP deneysel) veya sonraki sürümlerde T, diziler hariç herhangi bir temel olmayan tür olabilir.

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

5. Birlik türleri, Android 12 ve sonraki sürümlerde desteklenir.

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

İşlev bağımsız değişkenlerinin türlerini belirtirken, bunları in, out veya inout olarak belirtebilirsiniz. Bu, IPC çağrısı için hangi yön bilgisinin iletileceğini denetler. in varsayılan yöndür ve verilerin arayandan arayana iletildiğini gösterir. out, verilerin arayandan 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. Sürümlü bir arayüz ve daha eski bir çağrı sağlayıcıyla inout özelliğini kullanıyorsanız yalnızca arayanın içerdiği ek alanlar varsayılan değerlerine sıfırlanır. Rust ile ilgili olarak, normal bir inout türü &mut Vec<T> alırken, liste inout türü &mut Vec<T> alır.

UTF8/UTF16

C++ arka ucuyla dizelerin utf-8 veya utf-16 olmasını seçebilirsiniz. Dizeleri otomatik olarak utf-8 biçimine 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 AIDL'deki ek açıklamalar konusuna bakın.

Boş değer atanabilirliği

Java'da boş değerli olabilecek türlere@nullable null değerleri C++ ve NDK'ya göstermek için. Rust dili için bu @nullable türü, Option<T> olarak gösterilir. Yerel sunucular varsayılan olarak null değerleri reddeder. Bunun tek istisnası, her zaman boş olabilecek interface ve IBinder türleridir. nullable ek açıklaması hakkında daha fazla bilgi için AIDL'deki ek açıklamalar konusuna bakın.

Özel Ayrıştırılabilir Öğeler

Çekirdek derleme sistemindeki C++ ve Java arka uçlarında, bir hedef arka uçta (C++ veya Java'da) manuel olarak uygulanan bir ayrıştırılabilir öğe bildirebilirsiniz.

    package my.package;
    parcelable Foo;

veya C++ üst bilgisi bildirimi ile:

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

Daha sonra bu ayrıştırılabilirliği AIDL dosyalarında bir tür olarak kullanabilirsiniz, ancak AIDL tarafından oluşturulmaz.

Rust özel ayrıştırılabilir öğeleri desteklemiyor.

Varsayılan değerler

Yapılandırılmış ayrıştırılabilir öğeler, bu türden temel öğeler, String'ler ve 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 temel türler için sıfır, ilkel olmayan türler için null olarak başlatılır.

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

Hata İşleme

Android OS, hataları bildirirken hizmetler için 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. Bunların kullanımı AIDL tanımında iyi bir şekilde belgelenmiştir ve kullanıcı tanımlı durum veya dönüş türleri gerektirmez.

AIDL arayüzü, yerleşik hata türlerinin kapsamına girmeyen ek hata değerleri gerektiriyorsa hizmete özgü bir hatanın eklenmesine izin veren özel hizmete özgü yerleşik hatayı kullanabilir kullanıcı tarafından tanımlanan değerdir. 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şlenir. Java, hizmete özgü istisnalar için kullanıcı tanımlı hatayla birlikte android.os.ServiceSpecificException özelliğini kullanır.

Android'deki yerel kodda istisnalar kullanılmaz. 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 gösteren bunlardan birini döndürür. Rust arka ucu, NDK ile aynı istisna kodu değerlerini kullanır ancak bunları kullanıcıya iletmeden ö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ı tarafından tanımlanan 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
ABM binder/Status.h
Danimarka kronu android/binder_status.h
Paslı android/binder_status.h

Çeşitli arka uçları kullanma

Bu talimatlar Android platform koduna özeldir. Bu örneklerde, tanımlı bir tür kullanılmıştır: my.package.IFoo. Rust arka ucunun kullanımı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 bir arayüz, ayrıştırılabilir veya birleşim olsun, Java'da içe aktarabilirsiniz:

    import my.package.IFoo;

Veya CPP arka ucunda:

    #include <my/package/IFoo.h>

NDK arka ucunda da (ek aidl ad alanına dikkat edin):

    #include <aidl/my/package/IFoo.h>

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ü için üstbilgi eklemeniz gerekir. Örneğin, my/package/IFoo.aidl içinde tanımlanan iç içe yerleştirilmiş bir türü (Bar) 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>) tıklayın.

Hizmetleri uygulama

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

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

Java'da şu sınıftan çıkmanı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 (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;
    }

Paslı arka uçta:

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

Hizmetleri kaydettirme ve alma

Android platformundaki hizmetler genellikle servicemanager sürecine kaydedilir. Bazı API'ler aşağıdaki API'lere ek olarak hizmeti de kontrol eder (hizmet yoksa hemen dönerler). Tüm ayrıntılar için ilgili servicemanager arayüzüne göz atın. Bu işlemler yalnızca Android platformu üzerinde derleme yaparken yapılabilir.

Java'da:

    import android.os.ServiceManager;
    // registering
    ServiceManager.addService("service-name", myService);
    // getting
    myService = IFoo.Stub.asInterface(ServiceManager.getService("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);
    // getting
    status_t err = getService<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
    status_t err = AServiceManager_addService(myService->asBinder().get(), "service-name");
    // getting
    myService = IFoo::fromBinder(SpAIBinder(AServiceManager_getService("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(SpAIBinder(AServiceManager_waitForService("service-name")));

Paslı arka uçta:

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

Bağlayıcı barındıran bir hizmet öldüğünde bildirim almak için istek gönderebilirsiniz. Bu, geri çağırma proxy'lerinin sızdırılmasını önlemeye yardımcı olabilir veya hata kurtarmaya yardımcı olabilir. Bu çağrıları bağlayıcı proxy nesneleri üzerinde 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.
  • Paslı arka uçta bir DeathRecipient nesnesi oluşturun, ardından my_binder.link_to_death(&mut my_death_recipient) komutunu çağırın. DeathRecipient geri çağırmanın sahibi olduğundan, bildirimleri almak istediğiniz sürece bu nesneyi canlı tutmanız gerektiğini unutmayın.

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 bilgi toplar. AIDL hizmetleri için hata raporları, hizmet yöneticisine kayıtlı tüm hizmetlerdeki dumpsys ikili programını kullanarak bilgilerini hata raporuna aktarır. Ayrıca dumpsys SERVICE [ARGS] ile bir hizmetten bilgi almak için komut satırında dumpsys işlevini kullanabilirsiniz. C++ ve Java arka uçlarında, hizmetler için gerçekleştirilen dökümleri addService ek bağımsız değişkenleriyle kontrol edebilirsiniz. Hata ayıklama esnasında hizmetin PID'sini almak için dumpsys --pid SERVICE öğesini de kullanabilirsiniz.

Hizmetinize özel çıkış eklemek için sunucu nesnenizde bir AIDL dosyasında tanımlanan diğer herhangi bir IPC yöntemini uyguluyor gibi dump yöntemini geçersiz kılabilirsiniz. Bunu yaparken, döküm işlemlerini uygulama izniyle (android.permission.DUMP) veya belirli UID'lerle kısıtlamayı kısıtlamalısınız.

Java arka ucunda:

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

C++ 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ılan olarak izin vermek yerine):

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

Dinamik olarak arayüz tanımlayıcı alma

Arayüz açıklayıcı, bir arayüzün türünü tanımlar. Bu, hata ayıklanırken 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 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şlevi desteklemez.

İstatistiksel olarak arayüz tanımlayıcı alma

Bazen (örneğin, @VintfStability hizmetlerini kaydederken) arayüz tanımlayıcısının ne olduğunu statik olarak bilmeniz gerekir. Java'da, aşağıdaki gibi kod ekleyerek açıklayıcıyı alabilirsiniz:

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

CPP 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

Paslı arka uçta:

    aidl::my::package::BnFoo::get_descriptor()

Sıralama Aralığı

Yerel arka uçlarda, bir enum'un alabileceği olası değerleri tekrarlayabilirsiniz. Kod boyutuyla ilgili nedenlerden dolayı, bu özellik şu anda Java'da desteklenmemektedir.

AIDL olarak tanımlanan bir enum MyEnum için aşağıdaki şekilde yineleme sağlanır.

CPP arka ucunda:

    ::android::enum_range<MyEnum>()

NDK arka ucunda:

   ::ndk::enum_range<MyEnum>()

Paslı arka uçta:

    MyEnum::enum_range()
````

### Thread management {:#thread-management}

Every instance of `libbinder` in a process maintains one threadpool. For most
use cases, this should be exactly one threadpool, shared across all backends. The
only exception to this is when vendor code might load another copy of `libbinder`
to talk to `/dev/vndbinder`.  Since this is on a separate binder node, the
threadpool isn't shared.

For the Java backend, the threadpool can only increase in size (since it is
already started):

BinderInternal.setMaxThreads(<new larger value>);

For the C++ backend, the following operations are available:

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

Similarly, in the NDK backend:

bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
ABinderProcess_startThreadPool();
ABinderProcess_joinThreadPool();

In the Rust backend:

binder::ProcessState::start_thread_pool();
binder::add_service(“myservice”, my_service_binder).expect(“Failed to register service?”);
binder::ProcessState::join_thread_pool();