HAL'ler için AIDL

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. Mümkün olduğunda yalnızca AIDL'yi kullanmak için HAL'leri değiştirin (yukarı akış HAL'leri HIDL kullandığında, HIDL kullanılmalıdır).

system.img gibi çerçeve bileşenleri ile vendor.img gibi donanım bileşenleri arasında iletişim kurmak için AIDL kullanan HAL'ler, Kararlı AIDL kullanmalıdır. 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 var ve Android çerçeve bileşenleri veya uygulamalar gibi başka birçok yerde kullanılıyor. Artık AIDL kararlılık desteğine sahip 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, öğrenecek, hata ayıklayacak, optimize edecek ve güvence altına alacak tek bir şeye sahip olmak anlamına gelir.
  • AIDL, bir arabirimin sahipleri için yerinde sürüm oluşturmayı destekler:
    • Sahipler, arabirimlerin sonuna yöntemler veya parsellere alanlar ekleyebilir. Bu, kodun yıllar içinde 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 ekstra kitaplıklara gerek yoktur).
    • Uzantı arayüzleri, tip sisteminden ziyade çalışma zamanında eklenebilir, bu nedenle aşağı akış uzantılarını arayüzlerin daha yeni sürümlerine yeniden temellendirmeye gerek yoktur.
  • Mevcut bir AIDL arabirimi, sahibi onu dengelemeyi seçtiğinde doğrudan kullanılabilir. Önceden, arayüzün tüm bir kopyasının HIDL'de oluşturulması gerekiyordu.

AIDL HAL arabirimi yazma

AIDL arayüzünün sistem ve satıcı arasında kullanılması 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 bildiriminde olması gerekir. VTS testi vts_treble_vintf_vendor_test kullanarak bunu (ve serbest bırakılan arayüzlerin donmuş olduğunu doğrulamak gibi ilgili gereksinimleri) test edin. NDK arka ucunda AIBinder_forceDowngradeToLocalStability , C++ arka ucunda android::Stability::forceDowngradeToLocalStability veya gönderilmeden önce Java arka ucunda android.os.Binder#forceDowngradeToSystemStability çağırarak bir @VintfStability arayüzünü bu gereksinimler olmadan 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.

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 arayüzlerini bulma

AOSP Kararlı HAL'ler için AIDL arabirimleri, aidl klasörlerinde HIDL arabirimleriyle 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 diğer hardware/interfaces alt dizinlerine koymalısınız.

Genişletme Arayüzleri

Android, her sürümde bir dizi resmi AOSP arabirimine sahiptir. Android iş ortakları bu arabirimlere işlevsellik eklemek istediklerinde, bunları doğrudan değiştirmemelidir çünkü bu, Android çalışma zamanlarının AOSP Android çalışma zamanıyla uyumsuz olduğu 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 sağlayan şeydir.

Uzantılar iki farklı şekilde kaydedilebilir:

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

Bununla birlikte, bir uzantı kayıtlıysa, satıcıya özgü (yukarı akış AOSP'nin bir parçası olmadığı anlamına gelir) bileşenler arabirimi kullandığında, birleştirme çakışması olasılığı yoktur. Ancak, yukarı akış AOSP bileşenlerinde aşağı yönde değişiklikler yapıldığında, birleştirme çakışmaları meydana gelebilir ve aşağıdaki stratejiler önerilir:

  • arayüz eklemeleri bir sonraki sürümde AOSP'ye aktarılabilir
  • Birleştirme çakışmaları olmadan daha fazla esneklik sağlayan arabirim eklemeleri, bir sonraki sürümde yukarıya aktarılabilir

Uzatma Parcelables: ParcelableHolder

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

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

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, Android'in sonraki sürümlerinde Parcelable revize edildiğinde cihaz uygulayıcısı tarafından eklenen alanların çakışması olabileceğinden bu uygulama bozulur.

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

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

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

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. Kararlı AIDL'yi kullanmak için her zaman libbinder'ın sistem kopyasını system/lib*/libbinder.so adresinde kullanmalı ve /dev/binder üzerinde konuşmalısınız. Satıcı görüntüsündeki kod için bu, libbinder (VNDK'den) kullanılamayacağı anlamına gelir: bu kitaplığın kararsız bir C++ API'si ve kararsız dahili öğeleri vardır. Bunun yerine yerel satıcı kodu, AIDL'nin NDK arka ucunu, libbinder_ndk karşı bağlantıyı (ki sistem libbinder.so tarafından desteklenir) ve aidl_interface girişleri tarafından oluşturulan -ndk_platform kitaplıklarına karşı bağlantıyı kullanmalıdır.

AIDL HAL sunucusu örnek 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 bildirilmelidir, örneğin şu şekilde:

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

Aksi takdirde, normal olarak bir AIDL hizmeti kaydetmeleri gerekir. VTS testlerini çalıştırırken, beyan edilen tüm AIDL HAL'lerinin mevcut olması beklenir.

AIDL istemcisi yazmak

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'ı 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ına dayalı olarak .aidl dosyaları oluşturun
  • Tüm arka uçlar etkinken 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 onaylar oluşturun

Bir .hal dosyaları 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. Önceki sürümlerden eski dallardaki arabirimleri 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 paket ile yürütü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 önce düzeltilmesi gereken işlenmemiş sorunları içerir.
    • Oluşturulan .aidl dosyaları, eylem gerektirebilecek uyarılar ve öneriler içerebilir. Bu yorumlar // ile başlar.
    • Paketi temizleme ve iyileştirme yapma fırsatını kullanın.
    • 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, çalışma zamanını seçme bölümüne bakın.
    • Çeviri kitaplıklarını veya bunların oluşturduğu ve kullanılmayacak kodları kaldırın.
  5. Önemli AIDL/HIDL farklarına bakın.

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

AIDL HAL'leri için Sepolicy

Satıcı kodu tarafından görülebilen bir AIDL hizmet türü, hal_service_type özniteliğine sahip olmalıdır. Aksi takdirde, sepolicy yapılandırması diğer herhangi bir AIDL hizmetiyle aynıdır (ancak HAL'ler için özel nitelikler vardır). HAL hizmet bağlamının örnek bir 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 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 özgü 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 nitelikleri eklenmelidir. Belirli bir HAL özniteliği, birden çok hizmet türüyle ilişkilendirilebilir (az önce tartıştığımız gibi her birinin birden çok örneği olabilir). Bir HAL için foo elimizde hal_attribute(foo) var. Bu makro, hal_foo_client ve hal_foo_server özniteliklerini 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'ın istemcisi olması 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 etki alanı da yaratırız. Ancak, bazı cihazlar bu etki alanlarını kendi sunucuları için kullanır. Birden çok sunucu için etki alanları arasında ayrım yapmak, yalnızca aynı arayüze hizmet veren ve uygulamalarında farklı bir izin kümesine ihtiyaç duyan birden çok sunucumuz varsa ö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 çifti ile 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) öznitelik çifti) ilişkilendirmedik. Bir 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'ı ele geçirebileceği ve hal_foo_server işlemlerinin HAL'ı 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 özniteliklerine karşılık gelmeyebileceğine dikkat edin. Örneğin, hal_attribute_service(hal_foo, hal_foo2_service) görebiliriz. 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 çoğu HAL'nin nedeni, orijinal HAL öznitelik adının yeterince genel olmaması ve değiştirilememesidir.

Bunların hepsini bir araya getirdiğimizde, ö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, 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 uzantı arayüzleri

Doğrudan hizmet yöneticisine kayıtlı bir üst düzey arabirim veya bir alt arabirim olsun, herhangi bir bağlayıcı arabirime bir uzantı eklenebilir. Bir uzantı alırken, uzantı türünün beklendiği gibi olduğunu onaylamanız gerekir. Uzantılar yalnızca bir ciltleyiciye hizmet veren süreçten ayarlanabilir.

Ekli uzantılar, bir uzantı mevcut bir HAL'ın işlevselliğini değiştirdiğinde kullanılmalıdır. Tamamen yeni işlevsellik gerektiğinde, bu mekanizmanın kullanılmasına gerek yoktur ve bir uzantı arabirimi doğrudan hizmet yöneticisine kaydedilebilir. Ekli uzantı arabirimleri, alt arabirimlere eklendiğinde en mantıklıdır, çünkü bu hiyerarşiler derin veya çok örnekli olabilir. Başka bir hizmetin ciltleyici arabirim 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.

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
  • 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

İlgili arka uçtaki getExtension işlevinin belgelerinde bu API'ler hakkında daha fazla bilgi 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 arayüzlerini 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öz dizimi C++'a benzer.
  • Tüm AIDL arayüzlerinde yerleşik hata durumları vardır. Özel durum türleri oluşturmak yerine, arabirim dosyalarında sabit durum girişleri oluşturun ve CPP/NDK arka uçlarında EX_SERVICE_SPECIFIC ve Java arka ucunda ServiceSpecificException kullanın. Bkz. Hata İşleme .
  • Bağlayıcı nesneleri gönderildiğinde AIDL otomatik olarak iş parçacığı havuzlarını 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 bağımsız değişkenleri, output parametresine ek olarak in/out/inout olarak belirtilebilir ("eşzamanlı geri aramalar" yoktur).
  • AIDL, tanıtıcı yerine ilkel tür olarak 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.
  • AIDL varsayılan olarak gerçek zamanlı önceliği devralmaz. setInheritRt işlevi, gerçek zamanlı öncelik devralmayı etkinleştirmek için cilt başına kullanılmalıdır.