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
bildirimininstability: "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:
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
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
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
veyaequals
gibi gerekli olabilecek özellikler için@JavaDerive
ek 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
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'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_SPECIFIC
ve Java arka ucundaServiceSpecificException
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.