HAL'ler için AIDL

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

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

Motivasyon

AIDL, HIDL'den daha uzun süredir varlığını sürdürüyor ve Android çerçeve bileşenleri arasında veya uygulamalar gibi başka birçok yerde kullanılıyor. Artık AIDL'nin kararlılık desteği olduğuna göre, tek bir IPC çalışma zamanı ile tüm bir yığını uygulamak mümkündür. AIDL ayrıca HIDL'den daha iyi bir versiyonlama sistemine sahiptir.

  • Tek bir IPC dili kullanmak, öğrenilecek, hata ayıklanacak, optimize edilecek ve güvence altına alınacak tek bir şeyin olması anlamına gelir.
  • AIDL, bir arayüzün sahipleri için yerinde sürüm oluşturmayı destekler:
    • Sahipler, arayüzlerin sonuna yöntemler veya ayrıştırılabilirlere alanlar ekleyebilir. Bu, yıllar içinde kodu sürümlendirmenin 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 ekstra kitaplıklara gerek yoktur).
    • Uzantı arabirimleri, tür sistemi yerine çalışma zamanında eklenebilir; bu nedenle, aşağı yöndeki uzantıları, 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 arayüzü yazma

Sistem ile satıcı arasında kullanılacak bir AIDL arayüzü için arayüzün iki değişikliğe ihtiyacı vardır:

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

Bu değişiklikleri yalnızca arayüzün sahibi yapabilir.

Bu değişiklikleri yaptığınızda arayüzün çalışabilmesi için VINTF manifest dosyasında olması gerekir. VTS testini vts_treble_vintf_vendor_test kullanarak bunu (ve serbest bırakılan arayüzlerin dondurulduğunu doğrulamak gibi ilgili gereksinimleri) test edin. Gönderilmeden önce bir binder nesnesinde NDK arka ucunda AIBinder_forceDowngradeToLocalStability , C++ arka ucunda android::Stability::forceDowngradeToLocalStability veya Java arka ucunda android.os.Binder#forceDowngradeToSystemStability çağırarak bu gereksinimler olmadan bir @VintfStability arayüzünü kullanabilirsiniz. başka bir sürece. Tüm uygulamalar bir sistem bağlamında çalıştığından, bir hizmetin satıcı kararlılığına düşürülmesi Java'da desteklenmez.

Ayrıca maksimum kod taşınabilirliği ve gereksiz ek kitaplıklar gibi olası sorunları önlemek 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 CPP arka ucunun özel olarak nasıl seçileceğini anlatır.

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

AIDL HAL arayüzlerini bulma

HAL'ler için AOSP Kararlı AIDL arayüzleri, aidl klasörlerinde HIDL arayüzleriyle aynı temel dizinlerde bulunur.

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

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

Uzatma Arayüzleri

Android'in her sürümünde bir dizi resmi AOSP arayüzü bulunur. Android iş ortakları bu arayüzlere işlevsellik eklemek istediğinde bunları doğrudan değiştirmemelidir çü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 aynı zamanda GSI görüntüsünün çalışmaya devam etmesini sağlayan şeydir.

Uzantılar iki farklı şekilde kaydedilebilir:

  • çalışma zamanında ekteki uzantılara bakın.
  • bağımsız, dünya çapında ve VINTF'de kayıtlı.

Ancak bir uzantı kayıtlıysa, satıcıya özgü (yukarı akış AOSP'nin bir parçası olmadığı anlamına gelir) bileşenler arayüzü kullandığında, birleştirme çakışması olasılığı yoktur. Ancak, yukarı yöndeki AOSP bileşenlerinde aşağı yönde değişiklikler 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 aktarılabilir
  • Birleştirme çatışmaları olmadan daha fazla esneklik sağlayan arayüz eklemeleri bir sonraki sürümde yukarı akışa aktarılabilir

Uzatma Parcelable'lar: ParcelableHolder

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

Daha önce ParcelableHolder olmadan cihaz uygulayıcıları AOSP tanımlı kararlı bir AIDL arayüzünü değiştiremiyordu çünkü daha fazla alan eklemek 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 bozuldu çünkü cihaz uygulayıcısı tarafından eklenen alanlar, Android'in sonraki sürümlerinde Parcelable revize edildiğinde çakışmaya neden olabilir.

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

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

Daha sonra 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 göre geliştirme

AIDL'nin üç farklı arka ucu vardır: Java, NDK, CPP. Stabil AIDL'yi kullanmak için her zaman system/lib*/libbinder.so adresindeki libinder'ın sistem kopyasını kullanmalı ve /dev/binder üzerinden 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ığın kararsız bir C++ API'si ve kararsız dahili bileşenleri vardır. Bunun yerine, yerel satıcı kodunun AIDL'nin NDK arka ucunu kullanması, libbinder_ndk (sistem libbinder.so tarafından desteklenen) karşı bağlantıyı ve aidl_interface girişleri tarafından oluşturulan -ndk_platform kitaplıklarına karşı bağlantıyı kullanması gerekir.

AIDL HAL sunucu örneği adları

Geleneksel 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 örneğin şu şekilde bildirilmelidir:

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

Aksi takdirde, bir AIDL hizmetini normal şekilde kaydetmeleri gerekir. VTS testleri çalıştırılırken bildirilen tüm AIDL HAL'lerin mevcut olması beklenir.

AIDL istemcisi yazma

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

    <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 arayüzünü AIDL'ye dönüştürmek için hidl2aidl aracını kullanın.

hidl2aidl özellikleri:

  • Verilen paket için .hal dosyalarını temel alan .aidl dosyaları oluşturun
  • Yeni oluşturulan AIDL paketi için tüm arka uçların etkin olduğu 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 iddialar oluşturun

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

  1. system/tools/hidl/hidl2aidl konumunda bulunan aracı oluşturun.

    Bu aracı en son kaynaktan oluşturmak en eksiksiz deneyimi sağlar. Eski dallardaki arayüzleri önceki sürümlerden dönüştürmek için en son sürümü kullanabilirsiniz.

    m hidl2aidl
    
  2. Aracı, bir çıktı dizini ve ardından dönüştürülecek paketle çalıştırın.

    İsteğe bağlı olarak, yeni bir lisans dosyasının içeriğini oluşturulan tüm dosyaların üstüne eklemek için -l bağımsız değişkenini kullanın. Doğru lisansı ve tarihi kullandığınızdan emin olun.

    hidl2aidl -o <output directory> -l <file with license> <package>
    

    Örneğin:

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

    • conversion.log öncelikle düzeltilmesi gereken işlenmeyen sorunları içerir.
    • Oluşturulan .aidl dosyalarında işlem yapılması gerekebilecek uyarılar ve öneriler bulunabilir. Bu yorumlar // ile başlar.
    • Paketi temizleme ve iyileştirmeler yapma fırsatını değerlendirin.
    • toString veya equals gibi gerekli olabilecek özellikler için @JavaDerive ek açıklamasını kontrol edin.
  4. Yalnızca ihtiyacınız olan hedefleri oluşturun.

    • Kullanılmayacak arka uçları devre dışı bırakın. CPP arka ucu yerine NDK arka ucunu tercih edin, bkz . çalışma zamanını seçme .
    • Çeviri kitaplıklarını veya bunların oluşturduğu, kullanılmayacak herhangi bir kodu kaldırın.
  5. Bkz. Başlıca AIDL/HIDL farklılıkları .

    • AIDL'nin yerleşik Status ve istisnalarını kullanmak genellikle arayüzü iyileştirir ve arayüze özel başka bir durum türüne olan ihtiyacı ortadan kaldırır.
    • Yöntemlerdeki AIDL arabirimi bağımsız değişkenleri, HIDL'de olduğu gibi varsayılan olarak @nullable olamaz.

AIDL HAL'ler için Sepolicy

Satıcı kodu tarafından görülebilen bir AIDL hizmet tipinin hal_service_type niteliğine sahip olması gerekir. Aksi takdirde, sepolicy yapılandırması diğer AIDL hizmetleriyle aynıdır (HAL'ler için özel nitelikler olmasına rağmen). HAL hizmeti bağlamının örnek tanımı aşağıda verilmiştir:

    type hal_foo_service, service_manager_type, hal_service_type;

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

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

Yeni bir HAL türü oluşturduğumuzda HAL niteliklerinin eklenmesi gerekir. Belirli bir HAL özelliği birden fazla hizmet türüyle ilişkilendirilebilir (az önce tartıştığımız gibi her birinin birden fazla örneği olabilir). Bir HAL için foo , 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 özelliğiyle ilişkilendirir. Örneğin, bu HAL'in istemcisi olan sistem sunucusu hal_client_domain(system_server, hal_foo) politikasına karşılık gelir. Bir HAL sunucusu benzer şekilde hal_server_domain(my_hal_domain, hal_foo) içerir. Tipik olarak belirli bir HAL özelliği için referans veya örnek HAL'ler için hal_foo_default gibi bir alan da oluştururuz. Ancak bazı cihazlar bu alan adlarını kendi sunucuları için kullanır. Birden fazla sunucunun etki alanlarını ayırt etmek, yalnızca aynı arayüzü sunan birden fazla sunucumuz varsa ve bunların uygulamalarında farklı bir izin kümesine ihtiyaç duyuyorsak ö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 öznitelik grubuna atıfta bulunmak için kullanılır.

Ancak şu ana kadar hal_foo_service ve hal_foo ( hal_attribute(foo) 'dan gelen nitelik çifti) ilişkilendirmedik. Bir HAL özelliğ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'a erişebileceği ve hal_foo_server işlemlerinin HAL'i kaydedebileceği anlamına gelir. Bu kayıt kurallarının uygulanması içerik yöneticisi ( servicemanager ) tarafından yapılır. Hizmet adlarının her zaman HAL niteliklerine karşılık gelmeyebileceğini unutmayın. Örneğin hal_attribute_service(hal_foo, hal_foo2_service) ifadesini görebiliriz. Ancak genel olarak bu, hizmetlerin her zaman birlikte kullanıldığı anlamına geldiğinden, hal_foo2_service kaldırmayı ve tüm hizmet bağlamlarımız için hal_foo_service kullanmayı düşünebiliriz. Birden fazla hal_attribute_service ayarlayan HAL'lerin çoğu, orijinal HAL öznitelik adının yeterince genel olmaması ve değiştirilememesinden kaynaklanmaktadır.

Bunların hepsini bir araya getirdiğimizde örnek bir HAL şuna benzer:

    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, hal_service_type, 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_use(hal_foo_server)

    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
    hal_server_domain(some_hal_server_domain, hal_foo)

Ekli uzatma arayüzleri

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

Bir uzantı mevcut bir HAL'ın işlevselliğini değiştirdiğinde, ekli uzantılar kullanılmalıdır. Tamamen yeni bir işlevselliğe ihtiyaç duyulduğunda bu mekanizmanın kullanılmasına gerek yoktur ve bir uzantı arayüzü doğrudan servis yöneticisine kaydedilebilir. Ekli uzantı arayüzleri, alt arayüzlere eklendiklerinde en anlamlı hale gelir çünkü bu hiyerarşiler derin veya çok örnekli olabilir. Başka bir hizmetin ciltleyici 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ı defter tutmayı gerektirir.

Ciltleyicide 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
  • Rust arka ucunda: binder::Binder::set_extension

Bir ciltleyicide 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
  • Rust arka ucunda: binder::Binder::get_extension

Bu API'ler için daha fazla bilgiyi ilgili arka uçtaki getExtension işlevinin belgelerinde bulabilirsiniz. Uzantıların nasıl kullanılacağına ilişkin bir örnek hardware/interfaces/tests/extension/vibrator adresinde bulunabilir.

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

AIDL HAL'leri kullanırken veya AIDL HAL arayüzlerini kullanırken, HIDL HAL'leri yazmaya kıyasla farkların farkında olun.

  • AIDL dilinin sözdizimi Java'ya daha yakındır. HIDL sözdizimi C++'a benzer.
  • Tüm AIDL arayüzlerinde yerleşik hata durumları bulunur. Özel durum türleri oluşturmak yerine, arayüz dosyalarında sabit durum int'leri oluşturun ve CPP/NDK arka uçlarında EX_SERVICE_SPECIFIC ve Java arka ucunda ServiceSpecificException kullanın. Bkz. Hata İşleme .
  • AIDL, ciltleyici 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, kontrol edilmeyen aktarım hatalarında iptal edilmez (HIDL Return kontrol edilmeyen hatalarda iptal edilir).
  • AIDL dosya başına yalnızca bir tür bildirebilir.
  • AIDL argümanları, çıkış parametresine ek olarak giriş/çıkış/giriş olarak belirtilebilir ("senkronize geri çağırma" yoktur).
  • AIDL, tanıtıcı yerine ilkel tür olarak fd'yi kullanır.
  • HIDL, uyumsuz değişiklikler için ana sürümleri, uyumlu değişiklikler için ise küçük sürümleri kullanır. AIDL'de geriye dönük olarak uyumlu değişiklikler yapılmaktadır. AIDL'nin ana sürümlere ilişkin açık bir kavramı yoktur; bunun yerine bu, paket adlarına dahil edilir. Örneğin AIDL, bluetooth2 paket adını kullanabilir.
  • AIDL, varsayılan olarak gerçek zamanlı önceliği devralmaz. Gerçek zamanlı öncelik devralmayı etkinleştirmek için her ciltleyici için setInheritRt işlevi kullanılmalıdır.