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
@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 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
.hal
dosyalarını temel alan.aidl
dosyaları 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/hidl2aidl
konumunda 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
-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
öncelikle düzeltilmesi gereken işlenmeyen sorunları içerir. - Oluşturulan
.aidl
dosyaları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.
-
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, 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.
Bkz. Başlıca AIDL/HIDL farklılıkları .
- AIDL'nin yerleşik
Status
ve 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
@nullable
olamaz.
- 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_SPECIFIC
ve Java arka ucundaServiceSpecificException
kullanı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
Return
kontrol 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,
bluetooth2
paket 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
setInheritRt
işlevi kullanılmalıdır.