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
@VintfStabilityile açıklama eklenmelidir.
-
aidl_interfacebildiriminin
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
.haldosyalarına dayalı olarak
.aidldosyaları 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:
system/tools/hidl/hidl2aidlkonumunda 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
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
-lbağı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 email@example.com
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
.aidldosyaları, eylem gerektirebilecek uyarılar ve öneriler içerebilir. Bu yorumlar
//ile başlar.
- Paketi temizleme ve iyileştirme yapma fırsatını kullanın.
-
toStringveya
equalsgibi gerekli olabilecek özellikler için
@JavaDeriveek açıklamasını kontrol edin.
-
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.
Önemli AIDL/HIDL farklarına bakın.
- AIDL'nin yerleşik
Statusve 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
@nullabledeğildir.
- AIDL'nin yerleşik
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_SPECIFICve Java arka ucunda
ServiceSpecificExceptionkullanı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
Returndenetlenmeyen 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,
bluetooth2paket adını kullanabilir.
- AIDL varsayılan olarak gerçek zamanlı önceliği devralmaz.
setInheritRtişlevi, gerçek zamanlı öncelik devralmayı etkinleştirmek için cilt başına kullanılmalıdır.