HAL'ler için AIDL

Koleksiyonlar ile düzeninizi koruyun İçeriği tercihlerinize göre kaydedin ve kategorilere ayırın.

Android 11, Android'de HAL'ler için AIDL kullanma özelliğini sunar. Bu, Android'in bazı bölümlerini HIDL olmadan uygulamayı mümkün kılar. HAL'leri mümkün olduğunda yalnızca AIDL kullanacak şekilde geçiş yapın (yukarı akış HAL'leri HIDL kullandığında, HIDL kullanılmalıdır).

system.img dosyasındakiler gibi çerçeve bileşenleri ile seller.img içindekiler gibi donanım bileşenleri arasında iletişim kurmak için AIDL kullanan vendor.img , Kararlı AIDL kullanmalıdır. Ancak, örneğin bir HAL'den diğerine bir bölüm içinde iletişim kurmak için, kullanılacak IPC mekanizmasında herhangi bir kısıtlama yoktur.

Motivasyon

AIDL, HIDL'den daha uzun süredir var ve Android çerçeve bileşenleri veya uygulamalar gibi birçok başka yerde kullanılıyor. AIDL artık kararlılık desteğine sahip olduğundan, tek bir IPC çalışma zamanı ile tüm yığını uygulamak mümkündür. AIDL ayrıca HIDL'den daha iyi bir sürüm oluşturma sistemine sahiptir.

  • Tek bir IPC dili kullanmak, öğrenilecek, hata ayıklanacak, optimize edilecek ve güvenli hale getirilecek tek bir şeye sahip olmak anlamına gelir.
  • AIDL, bir arabirimin sahipleri için yerinde sürüm oluşturmayı destekler:
    • Sahipler, arayüzlerin sonuna yöntemler veya parsellenebilirlere alanlar ekleyebilir. Bu, yıllar içinde kodun sürümlendirilmesinin daha kolay olduğu ve ayrıca yıldan yıla maliyetin daha düşük olduğu anlamına gelir (türler yerinde değiştirilebilir ve her arayüz sürümü için fazladan kitaplığa gerek yoktur).
    • Uzantı arabirimleri, tür sistemi yerine çalışma zamanında eklenebilir, bu nedenle aşağı akış uzantılarını arabirimlerin daha yeni sürümlerine yeniden temellendirmeye gerek yoktur.
  • Mevcut bir AIDL arayüzü, sahibi onu stabilize etmeyi seçtiğinde doğrudan kullanılabilir. Daha önce, arayüzün tam bir kopyasının HIDL'de oluşturulması gerekiyordu.

AIDL HAL arabirimi yazma

Sistem ve satıcı arasında kullanılacak bir AIDL arabirimi için arabirimde iki değişiklik gerekir:

  • Her tür tanımına @VintfStability ile açıklama eklenmelidir.
  • aidl_interface bildiriminin stability: "vintf", .

Yalnızca bir arabirimin sahibi bu değişiklikleri yapabilir.

Bu değişiklikleri yaptığınızda, arabirimin çalışması için VINTF bildiriminde olması gerekir. VTS testi vts_treble_vintf_vendor_test kullanarak bunu (ve serbest bırakılan arabirimlerin donmuş olduğunu doğrulama gibi ilgili gereksinimleri) test edin. AIBinder_forceDowngradeToLocalStability önce NDK arka ucunda AIBinder_forceDowngradeToLocalStability, C++ arka android::Stability::forceDowngradeToLocalStability veya Java arka android.os.Binder#forceDowngradeToSystemStability öğesini çağırarak bir @VintfStability arabirimini bu gereksinimler olmadan kullanabilirsiniz. başka bir sürece. Tüm uygulamalar bir sistem bağlamında çalıştığından, bir hizmeti satıcı kararlılığına düşürmek Java'da desteklenmez.

Ek olarak, maksimum kod taşınabilirliği için ve gereksiz ek kitaplıklar gibi olası sorunlardan kaçınmak için CPP arka ucunu devre dışı bırakın.

Üç arka uç (Java, NDK ve CPP) olduğundan, aşağıdaki kod örneğinde backends kullanımının doğru olduğunu unutmayın. Aşağıdaki kod, devre dışı bırakmak için özel olarak CPP arka ucunun nasıl seçileceğini anlatır.

    aidl_interface: {
        ...
        backends: {
            cpp: {
                enabled: false,
            },
        },
    }

AIDL HAL arabirimlerini bulma

HAL'ler için AOSP Kararlı AIDL arabirimleri, aidl klasörlerinde HIDL arabirimleriyle aynı temel dizinlerdedir.

  • donanım/arayüzler
  • çerçeveler/donanım/arayüzler
  • sistem/donanım/arayüzler

Uzantı arayüzlerini, vendor veya hardware diğer hardware/interfaces alt dizinlerine koymalısınız.

Uzantı Arayüzleri

Android, her sürümde bir dizi resmi AOSP arayüzüne sahiptir. Android iş ortakları bu arayüzlere işlevsellik eklemek istediklerinde, bunları doğrudan değiştirmemelidirler çünkü bu, Android çalışma zamanlarının AOSP Android çalışma zamanı ile uyumlu olmadığı anlamına gelir. GMS cihazları için bu arayüzleri değiştirmekten kaçınmak, GSI görüntüsünün çalışmaya devam etmesini de sağlar.

Uzantılar iki farklı şekilde kaydedilebilir:

  • çalışma zamanında, ekli uzantılara bakın.
  • bağımsız, küresel olarak ve VINTF'de kayıtlı.

Ancak bir uzantı kaydedilir, satıcıya özel (yani yukarı akış AOSP'nin bir parçası olmayan) bileşenler arabirimi kullandığında, birleştirme çakışması olasılığı yoktur. Ancak, yukarı akış AOSP bileşenlerinde aşağı akış değişiklikleri yapıldığında, birleştirme çakışmaları ortaya çıkabilir ve aşağıdaki stratejiler önerilir:

  • arayüz eklemeleri bir sonraki sürümde AOSP'ye yüklenebilir
  • birleştirme çakışmaları olmadan daha fazla esneklik sağlayan arabirim eklemeleri, bir sonraki sürümde yukarı akışa alınabilir

Uzatma Parcelables: ParcelableHolder

ParcelableHolder , başka bir Parcelable içerebilen bir Parcelable . ParcelableHolder'ın ana kullanım durumu, ParcelableHolder genişletilebilir hale Parcelable . Örneğin, cihaz uygulayıcılarının AOSP tanımlı bir Parcelable , AospDefinedParcelable katma değerli özelliklerini içerecek şekilde genişletebilmeyi beklediği görüntü.

Daha önce ParcelableHolder olmadan, cihaz uygulayıcıları AOSP tanımlı kararlı bir AIDL arayüzünü değiştiremezdi çünkü daha fazla alan eklemek bir hata olurdu:

parcelable AospDefinedParcelable {
  int a;
  String b;
  String x; // ERROR: added by a device implementer
  int[] y; // added by a device implementer
}

Önceki kodda görüldüğü gibi, bu uygulama bozulur çünkü cihaz uygulayıcısı tarafından eklenen alanlar, Parcelable, Android'in sonraki sürümlerinde revize edildiğinde çakışabilir.

ParcelableHolder kullanarak, bir parsellenebilirin sahibi, bir Parcelable bir uzatma noktası tanımlayabilir.

parcelable AospDefinedParcelable {
  int a;
  String b;
  ParcelableHolder extension;
}

Ardından cihaz uygulayıcıları, uzantıları için kendi Parcelable tanımlayabilirler.

parcelable OemDefinedParcelable {
  String x;
  int[] y;
}

Son olarak, yeni Parcelable , ParcelableHolder alanı aracılığıyla orijinal Parcelable eklenebilir.


// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;

ap.extension.setParcelable(op);

...

OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);

// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();

ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);

...

std::shared_ptr<OemDefinedParcelable> op_ptr;

ap.extension.getParcelable(&op_ptr);

// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);

...

std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);

// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });

ap.extension.set_parcelable(Rc::clone(&op));

...

let op = ap.extension.get_parcelable::<OemDefinedParcelable>();

AIDL çalışma zamanına karşı oluşturma

AIDL'nin üç farklı arka ucu vardır: Java, NDK, CPP. Stable AIDL'yi kullanmak için, her zaman system/lib*/libbinder.so adresindeki libbinder'ın sistem kopyasını kullanmalı ve /dev/binder üzerinde konuşmalısınız. Satıcı görüntüsündeki kod için bu, libbinder (VNDK'dan) kullanılamayacağı anlamına gelir: bu kitaplık kararsız bir C++ API'sine ve kararsız dahili öğelere sahiptir. Bunun yerine, yerel satıcı kodu AIDL'nin NDK arka ucunu kullanmalı, libbinder_ndk (sistem libbinder.so tarafından desteklenmektedir) ve aidl_interface girdileri tarafından oluşturulan -ndk_platform kitaplıklarına karşı bağlantı kurmalıdır.

AIDL HAL sunucu örneği adları

Kural olarak, AIDL HAL hizmetleri $package.$type/$instance biçiminde bir örnek adına sahiptir. Örneğin, HAL vibratörünün bir örneği android.hardware.vibrator.IVibrator/default olarak kaydedilir.

AIDL HAL sunucusu yazma

@VintfStability AIDL sunucuları VINTF bildiriminde bildirilmelidir, örneğin şöyle:

    <hal format="aidl">
        <name>android.hardware.vibrator</name>
        <version>1</version>
        <fqname>IVibrator/default</fqname>
    </hal>

Aksi takdirde, normal olarak bir AIDL hizmetini kaydetmeleri gerekir. VTS testleri çalıştırılırken, bildirilen tüm AIDL HAL'lerinin kullanılabilir olması beklenir.

AIDL istemcisi yazma

AIDL istemcileri kendilerini uyumluluk matrisinde beyan etmelidir, örneğin şu şekilde:

    <hal format="aidl" optional="true">
        <name>android.hardware.vibrator</name>
        <version>1-2</version>
        <interface>
            <name>IVibrator</name>
            <instance>default</instance>
        </interface>
    </hal>

Mevcut bir HAL'yi HIDL'den AIDL'ye dönüştürme

Bir HIDL arabirimini AIDL'ye dönüştürmek için hidl2aidl aracını kullanın.

hidl2aidl özellikleri:

  • Verilen paket için .aidl dosyalarına dayalı .hal dosyaları oluşturun
  • Tüm arka uçların etkinleştirildiği yeni oluşturulan AIDL paketi için derleme kuralları oluşturun
  • HIDL türlerinden AIDL türlerine çeviri yapmak için Java, CPP ve NDK arka uçlarında çeviri yöntemleri oluşturun
  • Gerekli bağımlılıklara sahip çeviri kitaplıkları için derleme kuralları oluşturun
  • HIDL ve AIDL numaralandırıcılarının CPP ve NDK arka uçlarında aynı değerlere sahip olmasını sağlamak için statik onaylamalar oluşturun

Bir .hal dosyası paketini .aidl dosyalarına dönüştürmek için şu adımları izleyin:

  1. system/tools/hidl/hidl2aidl içinde bulunan aracı oluşturun.

    Bu aracı en son kaynaktan oluşturmak, en eksiksiz deneyimi sağlar. Önceki sürümlerden daha eski dallardaki arabirimleri dönüştürmek için en son sürümü kullanabilirsiniz.

    m hidl2aidl
    
  2. Aracı, dönüştürülecek paketin izlediği bir çıktı dizini ile çalıştırın.

    hidl2aidl -o <output directory> <package>
    

    Örneğin:

    hidl2aidl -o . android.hardware.nfc@1.2
    
  3. Oluşturulan dosyaları okuyun ve dönüştürmeyle ilgili sorunları düzeltin.

    • convert.log, önce conversion.log gereken tüm işlenmemiş sorunları içerir.
    • Oluşturulan .aidl dosyalarında işlem gerektirebilecek uyarılar ve öneriler olabilir. Bu yorumlar // ile başlar.
    • Paketi temizleme ve iyileştirmeler yapma fırsatını yakalayın.
  4. Yalnızca ihtiyacınız olan hedefleri oluşturun.

    • Kullanılmayacak arka uçları devre dışı bırakın. NDK arka ucunu CPP arka ucuna tercih edin, bkz. runtime seçme .
    • Çeviri kitaplıklarını veya bunların oluşturulup kullanılmayacak kodlarından herhangi birini kaldırın.
  5. Ana AIDL/HIDL farklılıklarına bakın.

    • AIDL'nin yerleşik Status ve istisnalarını kullanmak, tipik olarak arabirimi iyileştirir ve arabirime özgü başka bir durum türüne olan ihtiyacı ortadan kaldırır.

AIDL HAL'leri için Sepolicy

Satıcı kodu tarafından görülebilen bir AIDL hizmet türü, vendor_service özniteliğine sahip olmalıdır. Aksi takdirde, sepolicy yapılandırması diğer AIDL hizmetleriyle aynıdır (hal'ler için özel öznitelikler olsa da). HAL hizmet bağlamının örnek bir tanımı aşağıda verilmiştir:

    type hal_foo_service, service_manager_type, vendor_service;

Platform tarafından tanımlanan çoğu hizmet için, doğru türde bir hizmet bağlamı zaten eklenir (örneğin, android.hardware.foo.IFoo/default zaten hal_foo_service olarak işaretlenir). Ancak, bir çerçeve istemcisi birden çok örnek adını destekliyorsa, cihaza özel service_contexts dosyalarına ek örnek adları eklenmelidir.

    android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0

Yeni bir HAL türü oluşturduğumuzda HAL öznitelikleri eklenmelidir. Belirli bir HAL özniteliği, birden çok hizmet türüyle ilişkilendirilebilir (bunların her biri, az önce tartıştığımız gibi birden çok örneğe sahip olabilir). HAL, foo için hal_attribute(foo) var. Bu makro hal_foo_client ve hal_foo_server niteliklerini tanımlar. Belirli bir etki alanı için, hal_client_domain ve hal_server_domain makroları, bir etki alanını belirli bir HAL özniteliğiyle ilişkilendirir. Örneğin, sistem sunucusunun bu HAL'in istemcisi hal_client_domain(system_server, hal_foo) karşılık gelir. Bir HAL sunucusu benzer şekilde hal_server_domain(my_hal_domain, hal_foo) içerir. Tipik olarak, belirli bir HAL özniteliği için, referans veya örnek hal_foo_default için hal_foo_default gibi bir etki alanı da oluştururuz. Ancak bazı cihazlar bu alan adlarını kendi sunucuları için kullanır. Birden çok sunucu için etki alanları arasında ayrım yapmak, yalnızca aynı arabirime hizmet eden birden çok sunucumuz varsa ve uygulamalarında farklı bir izin kümesine ihtiyaç duyuyorsa önemlidir. Tüm bu makrolarda hal_foo aslında bir sepolicy nesnesi değildir. Bunun yerine, bu belirteç, bu makrolar tarafından, bir istemci sunucu çiftiyle ilişkili nitelikler grubuna başvurmak için kullanılır.

Ancak şu ana kadar hal_foo_service ve hal_foo ( hal_attribute(foo) gelen öznitelik çifti) ilişkilendirmedik. HAL özniteliği, hal_attribute_service makrosu kullanılarak AIDL HAL hizmetleriyle ilişkilendirilir (HIDL HAL'ler hal_attribute_hwservice makrosunu kullanır). Örneğin, hal_attribute_service(hal_foo, hal_foo_service) . Bu, hal_foo_client işlemlerinin HAL'i ele geçirebileceği ve hal_foo_server işlemlerinin HAL'ı kaydedebileceği anlamına gelir. Bu kayıt kurallarının uygulanması, bağlam yöneticisi ( servicemanager ) tarafından yapılır. Dikkat edin, hizmet adları her zaman HAL niteliklerine karşılık gelmeyebilir. Örneğin, hal_attribute_service(hal_foo, hal_foo2_service) görebiliriz. Genel olarak, bu, hizmetlerin her zaman birlikte kullanıldığını ima hal_foo_service , tüm hizmet bağlamlarımız için hal_foo2_service'i kaldırmayı ve hal_foo2_service kullanmayı düşünebiliriz. Birden çok hal_attribute_service ayarlayan HAL'lerin çoğu, orijinal HAL öznitelik adının yeterince genel olmaması ve değiştirilememesinden kaynaklanır.

Bunların hepsini bir araya getirirsek, örnek bir HAL şöyle görünür:

    public/attributes:
    // define hal_foo, hal_foo_client, hal_foo_server
    hal_attribute(foo)

    public/service.te
    // define hal_foo_service
    type hal_foo_service, vendor_service, protected_service, service_manager_type

    public/hal_foo.te:
    // allow binder connection from client to server
    binder_call(hal_foo_client, hal_foo_server)
    // allow client to find the service, allow server to register the service
    hal_attribute_service(hal_foo, hal_foo_service)
    // allow binder communication from server to service_manager
    binder_call(hal_foo_server, servicemanager)

    private/service_contexts:
    // bind an AIDL service name to the selinux type
    android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0

    private/<some_domain>.te:
    // let this domain use the hal service
    binder_use(some_domain)
    hal_client_domain(some_domain, hal_foo)

    vendor/<some_hal_server_domain>.te
    // let this domain serve the hal service
    binder_use(some_hal_server_domain)
    hal_server_domain(some_hal_server_domain, hal_foo)

Ekli uzatma arayüzleri

İster doğrudan servis yöneticisine kayıtlı bir üst düzey arayüz isterse bir alt arayüz olsun, herhangi bir bağlayıcı arayüzüne bir uzantı eklenebilir. Bir uzantı alırken, uzantının türünün beklendiği gibi olduğunu onaylamanız gerekir. Uzantılar yalnızca bir bağlayıcıya hizmet eden süreçten ayarlanabilir.

Bir uzantı mevcut bir HAL'nin işlevselliğini değiştirdiğinde, ekli uzantılar kullanılmalıdır. Tamamen yeni bir işlevsellik gerektiğinde, bu mekanizmanın kullanılması gerekmez ve doğrudan hizmet yöneticisine bir uzantı arabirimi kaydedilebilir. Ekli uzantı arabirimleri, alt arabirimlere eklendiklerinde en mantıklıdır, çünkü bu hiyerarşiler derin veya çok örnekli olabilir. Başka bir hizmetin bağlayıcı arabirimi hiyerarşisini yansıtmak için genel bir uzantı kullanmak, doğrudan eklenen uzantılara eşdeğer işlevsellik sağlamak için kapsamlı muhasebe gerektirir.

Bağlayıcıda bir uzantı ayarlamak için aşağıdaki API'leri kullanın:

  • NDK arka ucunda: AIBinder_setExtension
  • Java arka ucunda: android.os.Binder.setExtension
  • CPP arka ucunda: android::Binder::setExtension

Bir bağlayıcıda uzantı almak için aşağıdaki API'leri kullanın:

  • NDK arka ucunda: AIBinder_getExtension
  • Java arka ucunda: android.os.IBinder.getExtension
  • CPP arka ucunda: android::IBinder::getExtension

Bu API'ler için daha fazla bilgiyi ilgili arka getExtension işlevinin belgelerinde bulabilirsiniz. Uzantıların nasıl kullanılacağına ilişkin bir örnek donanım/arayüzler/testler/uzantı/vibratör içinde bulunabilir.

Başlıca AIDL/HIDL farklılıkları

AIDL HAL'leri veya AIDL HAL arabirimlerini kullanırken, HIDL HAL'leri yazmaya kıyasla farklılıkların farkında olun.

  • AIDL dilinin sözdizimi Java'ya daha yakındır. HIDL sözdizimi C++'a benzer.
  • Tüm AIDL arabirimlerinde yerleşik hata durumları vardır. Özel durum türleri oluşturmak yerine, arabirim dosyalarında sabit durum bilgileri oluşturun ve CPP/NDK arka EX_SERVICE_SPECIFIC ve Java arka ucunda ServiceSpecificException kullanın. Hata İşleme konusuna bakın.
  • AIDL, bağlayıcı nesneler gönderildiğinde iş parçacığı havuzlarını otomatik olarak başlatmaz. Manuel olarak başlatılmaları gerekir (bkz. iş parçacığı yönetimi ).
  • AIDL, denetlenmeyen taşıma hatalarında iptal etmez (HIDL Return , denetlenmeyen hatalarda iptal eder).
  • AIDL, dosya başına yalnızca bir tür bildirebilir.
  • AIDL argümanları, çıktı parametresine ek olarak giriş/çıkış/giriş olarak belirtilebilir ("eşzamanlı geri aramalar" yoktur).
  • AIDL, ilkel tür olarak tutamaç yerine bir fd kullanır.
  • HIDL, uyumsuz değişiklikler için ana sürümleri ve uyumlu değişiklikler için küçük sürümleri kullanır. AIDL'de geriye dönük uyumlu değişiklikler yerinde yapılır. AIDL'nin açık bir ana sürüm kavramı yoktur; bunun yerine, bu paket adlarına dahil edilmiştir. Örneğin, AIDL bluetooth2 paket adını kullanabilir.