Android 11, Android'de HAL'ler için AIDL kullanma özelliğini sunar. Bu da Android'in bazı parçalarını HIDL olmadan uygulayabilmenizi sağlar. Mümkün olduğunda yalnızca AIDL kullanılacak geçiş HAL'leri (yukarı akış HAL'leri HIDL kullandığında, HIDL kullanılmalıdır).
system.img
içindekiler 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. Bununla birlikte, bir bölümde (örneğin, bir HAL'den diğerine) iletişim kurmak için kullanılacak IPC mekanizmasına dair bir kısıtlama yoktur.
Motivasyon
AIDL, HIDL'den daha uzun süredir kullanımdadır ve Android çerçeve bileşenleri veya uygulamalar gibi farklı yerlerde kullanılmaktadır. Artık AIDL kararlılık desteğine sahip olduğundan, tek bir IPC çalışma zamanıyla tüm yığını uygulamak mümkündür. AIDL ayrıca HIDL'den daha iyi bir sürüm oluşturma sistemine sahip.
- Tek bir IPC dili kullanmak; öğrenmek, hata ayıklamak, optimize etmek ve güvence altına almak için tek bir şeye sahip olmak anlamına gelir.
- AIDL, arayüz sahipleri için yerinde sürüm oluşturmayı destekler:
- Sahipler, arayüzlerin sonuna yöntemler veya parseller için alanlar ekleyebilir. Bu da, kodun yıllar içinde daha kolay bir şekilde oluşturulmasını ve aynı zamanda yıllık maliyetin daha düşük olması anlamına gelir. (Türler, yerinde değiştirilebilir ve her bir arayüz sürümü için ek kitaplıklara gerek yoktur).
- Uzantı arayüzleri, tür sistemi yerine çalışma zamanında eklenebilir. Bu nedenle, aşağı akış uzantılarının yeni arayüz sürümlerine yeniden yerleştirilmesine gerek yoktur.
- Mevcut bir AIDL arayüzü, sahibi tarafından sabitlemeyi seçtiğinde doğrudan kullanılabilir. Daha önce, arayüzün tamamının HIDL'de oluşturulması gerekiyordu.
AIDL çalışma zamanına göre geliştirme yapın
AIDL'nin üç farklı arka ucu vardır: Java, NDK ve CPP. Kararlı AIDL'yi kullanmak için her zaman system/lib*/libbinder.so
adresindeki libbinder'ın sistem kopyasını kullanmanız ve /dev/binder
üzerinde konuşmanız gerekir. Tedarikçi firma görüntüsündeki kod için bu, libbinder
(VNDK'dan) kullanılamaz: Bu kitaplıkta kararsız bir C++ API'si ve kararsız dahili öğeleri vardır. Bunun yerine, yerel tedarikçi firma kodu AIDL'nin NDK arka ucunu kullanmalı, libbinder_ndk
(libbinder.so
sistemi tarafından desteklenir) ile bağlantı kurmalı ve aidl_interface
girişleri tarafından oluşturulan NDK kitaplıklarına bağlanmalıdır. Tam modül adları için modül adlandırma kurallarına bakın.
AIDL HAL arayüzü yazma
AIDL arayüzünün sistem ve satıcı arasında kullanılması için arayüzde iki değişiklik yapılması gerekir:
- Her tür tanımı
@VintfStability
ile açıklanmalıdır. aidl_interface
beyanındastability: "vintf",
bulunmalıdır.
Bu değişiklikleri yalnızca arayüzün sahibi yapabilir.
Bu değişiklikleri yaptığınızda arayüzün çalışması için VINTF manifest'inde olması gerekir. vts_treble_vintf_vendor_test
VTS testini kullanarak bunu (ve serbest bırakılan arayüzlerin dondurulduğunu doğrulama gibi ilgili şartları) test edin. Bu gereksinimler olmadan bir @VintfStability
arayüzünü, başka bir işleme gönderilmeden önce NDK arka ucunda AIBinder_forceDowngradeToLocalStability
, C++ arka ucunda android::Stability::forceDowngradeToLocalStability
ya da Java arka ucunda android.os.Binder#forceDowngradeToSystemStability
çağrısı yaparak kullanabilirsiniz. Tüm uygulamalar bir sistem bağlamında çalıştığından, bir hizmetin tedarikçi firma kararlılığına düşürülmesi Java'da desteklenmez.
Ayrıca, maksimum kod taşınabilirliği sağlamak ve gereksiz ek kitaplıklar gibi olası sorunlardan kaçınmak için PPP 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 kodda, devre dışı bırakmak için PBM arka ucunun nasıl seçileceği açıklanmaktadır.
aidl_interface: {
...
backends: {
cpp: {
enabled: false,
},
},
}
AIDL HAL arayüzlerini bulma
HAL'ler için AOSP Kararlı AIDL arayüzleri, aidl
klasörlerde 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 yerleştirmeniz gerekir.
Uzantı arayüzleri
Android'in her sürümde bir dizi resmi AOSP arayüzü vardır. Android iş ortakları bu arayüzlere işlev eklemek istediklerinde, bunları doğrudan değiştirmemelidirler. Bu, Android çalışma zamanlarının AOSP Android çalışma zamanıyla uyumlu olmadığı anlamına gelir. GMS cihazlarda, bu arayüzleri değiştirmekten kaçınmak da GSI görüntüsünün çalışmaya devam etmesini sağlar.
Uzantılar iki farklı şekilde kaydedilebilir:
- için ekteki uzantılara bakın.
- dünya genelinde ve VINTF'de kayıtlı olmalıdır.
Bununla birlikte, tedarikçi firmaya özgü (yukarı akış AOSP'nin bir parçası olmayan) bileşenler arayüzü kullandığında uzantı kaydedilir, birleştirme çakışması olasılığı olmaz. 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 eklemelerinin bir sonraki sürümde AOSP'ye yayınlanması
- birleştirme çakışmaları olmadan daha fazla esneklik sağlayan arayüz eklemeleri bir sonraki sürümde yayınlanabilecek
Uzantı paketleri: ParcelableHolder
ParcelableHolder
, başka bir Parcelable
içerebilen bir Parcelable
.
ParcelableHolder
öğesinin ana kullanım alanı, Parcelable
uzantısını genişletilebilir hale getirmektir.
Örneğin, cihaz uygulayıcılarının AOSP tarafından tanımlanan Parcelable
, AospDefinedParcelable
öğesini, katma değerli özelliklerini içerecek şekilde genişletmesini beklediği görüntü.
Daha önce ParcelableHolder
olmadan, cihaz uygulayıcıları daha fazla alan eklemek hata olacağından AOSP tarafından tanımlanan kararlı bir AIDL arayüzünü değiştiremiyordu:
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, cihaz uygulayıcısı tarafından eklenen alanlar, Parcelable'ın Android'in sonraki sürümlerinde revize edilmesi sırasında bir çakışma yaşayabileceği için bu uygulama ihlal edilmiştir.
Ayrıştırılabilir öğenin sahibi, ParcelableHolder
kullanarak Parcelable
içinde bir uzantı noktası tanımlayabilir.
parcelable AospDefinedParcelable {
int a;
String b;
ParcelableHolder extension;
}
Daha sonra, cihaz uygulayıcıları uzantıları için kendi Parcelable
değerlerini tanımlayabilir.
parcelable OemDefinedParcelable {
String x;
int[] y;
}
Son olarak, yeni Parcelable
, orijinal Parcelable
öğesine ParcelableHolder
alanıyla 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 HAL sunucusu örneği adları
Geleneksel olarak, AIDL HAL hizmetleri $package.$type/$instance
biçiminde bir örnek adına sahiptir. Örneğin, vibratör HAL'nin bir örneği android.hardware.vibrator.IVibrator/default
olarak kaydedilir.
AIDL HAL sunucusu yazma
@VintfStability
AIDL sunucuları VINTF manifest'inde tanımlanmalıdır. Örneğin:
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>1</version>
<fqname>IVibrator/default</fqname>
</hal>
Aksi takdirde, AIDL hizmetini normal şekilde kaydetmeleri gerekir. VTS testleri çalıştırırken beyan edilen tüm AIDL HAL'lerin kullanılabilir olması beklenir.
AIDL istemcisi yaz
AIDL istemcileri, uyumluluk matrisinde kendilerini beyan etmelidir. Örneğin:
<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
HIDL arayüzünü AIDL'ye dönüştürmek için hidl2aidl
aracını kullanın.
hidl2aidl
özellikleri:
- Belirtilen paket için
.hal
dosyalarına dayalı.aidl
dosyaları oluşturun - Yeni oluşturulan AIDL paketi için tüm arka uçlar etkin şekilde derleme kuralları oluşturun
- HIDL türlerinden AIDL türlerine çeviri için Java, CPP ve NDK arka uçlarında çeviri yöntemleri oluşturma
- Gerekli bağımlılıklara sahip çeviri kitaplıkları için derleme kuralları oluşturma
- HIDL ve AIDL numaralandırıcılarının PPP 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 aşağıdaki adımları uygulayın:
system/tools/hidl/hidl2aidl
adresindeki aracı oluşturun.Bu aracı en güncel kaynaktan oluşturmak en eksiksiz deneyimi sunar. Önceki sürümlerdeki eski şubelerdeki arayüzleri dönüştürmek için en yeni sürümü kullanabilirsiniz.
m hidl2aidl
Aracı, dönüştürülecek paketin ardından bir çıkış diziniyle yürütün.
İsteğe bağlı olarak, yeni lisans dosyasının içeriğini oluşturulan tüm dosyaların en ü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>
Örnek:
hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
Oluşturulan dosyaları okuyun ve dönüşümle ilgili sorunları giderin.
conversion.log
, öncelikle düzeltilmesi gereken sorunları içeriyor.- Oluşturulan
.aidl
dosyalarında işlem gerekebilecek uyarı ve öneriler bulunabilir. Bu yorumlar//
ile başlıyor. - Paketi temizleme ve pakette iyileştirmeler yapma fırsatından yararlanabilirsiniz.
toString
veyaequals
gibi gerekli olabilecek özellikler için@JavaDerive
notunu kontrol edin.
Yalnızca ihtiyacınız olan hedefleri oluşturun.
- Kullanılmayan arka uçları devre dışı bırakın. CPP arka ucu yerine NDK arka ucunu tercih edin. Çalışma zamanı seçme bölümüne bakın.
- Çeviri kitaplıklarını veya kullanılmayacak olan kodları kaldırın.
Önemli AIDL/HIDL farkları konusuna bakın.
- AIDL'nin yerleşik
Status
ve istisnalarının kullanılması genellikle arayüzü iyileştirir ve arayüze özgü başka bir durum türüne olan ihtiyacı ortadan kaldırır. - Yöntemlerdeki AIDL arayüzü bağımsız değişkenleri, HIDL'deki gibi varsayılan olarak
@nullable
değildir.
- AIDL'nin yerleşik
AIDL HAL'lere ilişkin SEPolitikası
Tedarikçi firma kodu tarafından görülebilen bir AIDL hizmet türünün hal_service_type
özelliği olmalıdır. Aksi takdirde, sepolicy yapılandırması diğer AIDL hizmetleriyle aynıdır (ancak HAL'ler için özel özellikler vardır). Aşağıda, HAL hizmeti bağlamının örnek bir tanımı 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 fazla ö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 özellikleri eklenmelidir. Belirli bir HAL özelliği, birden çok hizmet türüyle ilişkilendirilebilir (her birinin, az önce konuştuğumuz gibi birden fazla örneği olabilir). HAL, foo
için hal_attribute(foo)
. Bu makro hal_foo_client
ve hal_foo_server
özelliklerini tanımlar. Belirli bir alan adında hal_client_domain
ve hal_server_domain
makroları, alanı belirli bir HAL özelliğiyle ilişkilendirir. Örneğin, sistem sunucusunun bu HAL'nin istemcisi olması, hal_client_domain(system_server, hal_foo)
politikasına karşılık gelir. Benzer şekilde bir HAL sunucusunda hal_server_domain(my_hal_domain, hal_foo)
bulunur. Genellikle, belirli bir HAL özelliği için referans veya örnek HAL'ler olarak hal_foo_default
gibi bir alan adı da oluştururuz. Ancak bazı cihazlar bu alan adlarını kendi sunucuları için kullanır.
Birden fazla sunucunun alanlarını ayırt etmek, yalnızca aynı arayüzü sunan birden fazla sunucumuz olduğunda ve bunların uygulamalarında farklı bir izne ihtiyaç duyulduğunda önemlidir. Bu makroların tümünde hal_foo
, aslında bir sepolicy nesnesi değildir. Bunun yerine, bu jeton bu makrolar tarafından, bir istemci sunucu çiftiyle ilişkilendirilmiş özellik grubuna başvurmak için kullanılır.
Ancak şu ana kadar hal_foo_service
ve hal_foo
öğelerini (hal_attribute(foo)
alanındaki özellik çifti) ilişkilendirmedik. HAL özelliği, hal_attribute_service
makrosu kullanılarak AIDL HAL hizmetleriyle ilişkilendirilir (HIDL HAL'leri hal_attribute_hwservice
makrosu kullanır). Örneğin, hal_attribute_service(hal_foo, hal_foo_service)
. Bu, hal_foo_client
işlemlerinin HAL'yi ele alabileceği ve hal_foo_server
işlemlerinin HAL'yi kaydedebileceği anlamına gelir. Bu kayıt kurallarının uygulanması bağlam yöneticisi (servicemanager
) tarafından yapılır. Hizmet adlarının her zaman HAL özelliklerine karşılık gelmeyebileceğini unutmayın. Örneğin, şunu görebiliriz: hal_attribute_service(hal_foo, hal_foo2_service)
. Ancak genellikle bu durum, hizmetlerin her zaman birlikte kullanıldığını ima ettiğinden hal_foo2_service
etiketini kaldırıp tüm hizmet bağlamlarımız için hal_foo_service
etiketini kullanabiliriz. Birden fazla hal_attribute_service
ayarlayan HAL'lerin çoğu, orijinal HAL özellik adının yeterince genel olmaması ve değiştirilememesinden kaynaklanır.
Hepsini bir araya getirdiğimizde, bir HAL örneği şu şekilde olacaktı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
Bir uzantı, doğrudan hizmet yöneticisine kayıtlı üst düzey bir arayüz veya bir alt arayüz olması fark etmeksizin tüm bağlayıcı arayüzlere eklenebilir. Uzantı alırken, uzantı türünün beklendiği gibi olduğunu onaylamanız gerekir. Uzantılar, yalnızca bağlayıcı sunan işlemden ayarlanabilir.
Ekli uzantılar, bir uzantı mevcut bir HAL'nin işlevini değiştirdiğinde kullanılmalıdır. Tamamen yeni bir işlev gerektiğinde bu mekanizmanın kullanılması gerekmez ve bir uzantı arayüzü doğrudan hizmet yöneticisine kaydedilebilir. Derin veya çok örnekli olabileceğinden, ekli uzantı arayüzleri, alt arayüzlere bağlı olduklarında daha anlamlı olur. Başka bir hizmetin bağlayıcı arayüzü hiyerarşisini yansıtmak için global uzantı kullanmak, doğrudan eklenen uzantılara eşdeğer işlevler sağlamak için kapsamlı muhasebe işlemleri 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
- PBM arka ucunda:
android::Binder::setExtension
- Rust arka ucunda:
binder::Binder::set_extension
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
- PBM arka ucunda:
android::IBinder::getExtension
- Rust arka ucunda:
binder::Binder::get_extension
İlgili arka uçtaki getExtension
işlevinin dokümanlarında bu API'ler hakkında daha fazla bilgi bulabilirsiniz. Uzantıların nasıl kullanılacağına ilişkin bir örneği donanım/arayüzler/testler/uzantı/titreşim bölümünde bulabilirsiniz.
Önemli AIDL ve HIDL farklılıkları
AIDL HAL veya AIDL HAL arayüzleri kullanırken, HIDL HAL yazmayla karşılaştırıldığında farklılıklara dikkat edin.
- AIDL dilinin söz dizimi Java'ya daha yakındır. HIDL söz dizimi C++ ile 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 ucundaServiceSpecificException
kullanın. Hata İşleme bölümüne bakın. - AIDL, bağlayıcı nesneleri gönderildiğinde iş parçacığı havuzlarını otomatik olarak başlatmaz. Bunların manuel olarak başlatılması gerekir (ileti dizisi yönetimine bakın).
- AIDL, kontrol edilmemiş aktarım hatalarında iptal etmez (HIDL
Return
, kontrol edilmemiş hatalarda iptal eder). - AIDL dosya başına yalnızca bir tür tanımlayabilir.
- AIDL bağımsız değişkenleri, çıkış parametresine ek olarak giriş/çıkış/inout olarak belirtilebilir ("eşzamanlı geri çağırma" yoktur).
- AIDL, temel tür olarak herkese açık kullanıcı adı yerine fd kullanır.
- HIDL, uyumsuz değişiklikler için ana sürümleri, uyumlu değişiklikler için ise alt sürümleri kullanır. AIDL'de geriye dönük uyumlu değişiklikler uygulanır.
AIDL'de ana sürümler konusunda açık bir kavram yoktur. Bunun yerine bu kavram paket adlarına dahil edilmiştir. Örneğin, AIDL paket adını
bluetooth2
kullanabilir. - AIDL varsayılan olarak gerçek zamanlı önceliği devralmaz.
setInheritRt
işlevi, gerçek zamanlı öncelik devralmayı etkinleştirmek için bağlayıcı başına kullanılmalıdır.