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
@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 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
.haldosyalarını temel alan
.aidldosyaları 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:
system/tools/hidl/hidl2aidlkonumunda 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
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
-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öncelikle düzeltilmesi gereken işlenmeyen sorunları içerir.
- Oluşturulan
.aidldosyaları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.
-
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, 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.
- AIDL'nin yerleşik
Statusve 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
@nullableolamaz.
- AIDL'nin yerleşik
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_SPECIFICve Java arka ucunda
ServiceSpecificExceptionkullanı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
Returnkontrol 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,
bluetooth2paket 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
setInheritRtişlevi kullanılmalıdır.