kararlı AIDL

Android 10, AIDL arabirimleri tarafından sağlanan uygulama program arabirimini (API)/uygulama ikili arabirimini (ABI) takip etmenin yeni bir yolu olan kararlı Android Arabirim Tanımlama Dili (AIDL) için destek ekler. Kararlı AIDL, AIDL'den aşağıdaki temel farklılıklara sahiptir:

  • Arayüzler, yapı sisteminde aidl_interfaces ile tanımlanır.
  • Arayüzler yalnızca yapılandırılmış veriler içerebilir. İstenen türleri temsil eden parseller, AIDL tanımlarına göre otomatik olarak oluşturulur ve otomatik olarak sıralanır ve sıralanmaz.
  • Arabirimler kararlı (geriye dönük uyumlu) olarak bildirilebilir. Bu olduğunda, API'leri AIDL arayüzünün yanındaki bir dosyada izlenir ve sürümlenir.

Bir AIDL arayüzü tanımlama

aidl_interface tanımı şöyle görünür:

aidl_interface {
    name: "my-aidl",
    srcs: ["srcs/aidl/**/*.aidl"],
    local_include_dir: "srcs/aidl",
    imports: ["other-aidl"],
    versions_with_info: [
        {
            version: "1",
            imports: ["ohter-aidl-V1"],
        },
        {
            version: "2",
            imports: ["other-aidl-V3"],
        }
    ],
    stability: "vintf",
    backend: {
        java: {
            enabled: true,
            platform_apis: true,
        },
        cpp: {
            enabled: true,
        },
        ndk: {
            enabled: true,
        },
        rust: {
            enabled: true,
        },
    },

}
  • name : Bir AIDL arabirimini benzersiz şekilde tanımlayan AIDL arabirim modülünün adı.
  • srcs : Arayüzü oluşturan AIDL kaynak dosyalarının listesi. com.acme paketinde tanımlanan AIDL türü Foo yolu, <base_path>/com/acme/Foo.aidl olmalıdır; burada <base_path> , Android.bp bulunduğu dizinle ilgili herhangi bir dizin olabilir. Yukarıdaki örnekte, <base_path> srcs/aidl .
  • local_include_dir : Paket adının başladığı yol. Yukarıda açıklanan <base_path> karşılık gelir.
  • imports : Bunun kullandığı aidl_interface modüllerinin listesi. AIDL arabirimlerinizden biri başka bir aidl_interface bir arabirim veya ayrıştırılabilir kullanıyorsa, adını buraya yazın. Bu, en son sürüme atıfta bulunmak için tek başına ad veya belirli bir sürüme atıfta bulunmak için sürüm sonekine sahip ad (örneğin -V1 ) olabilir. Sürüm belirleme, Android 12'den beri desteklenmektedir
  • versions : Arayüzün api_dir altında dondurulmuş önceki sürümleri, Android 11'den başlayarak, versions aidl_api/ name altında dondurulmuştur. Bir arayüzün donmuş versiyonu yoksa, bu belirtilmemeli ve uyumluluk kontrolleri yapılmayacaktır. Bu alan, 13 ve üstü için versions_with_info ile değiştirilmiştir.
  • versions_with_info : Her biri donmuş bir sürümün adını ve bu aidl_interface sürümünün içe aktardığı diğer aidl_interface modüllerinin içe aktarılan sürümlerini içeren bir liste içeren demetlerin listesi. Bir AIDL arabirimi IFACE'nin V sürümünün tanımı aidl_api/ IFACE / V bulunur. Bu alan Android 13'te tanıtıldı ve doğrudan Android.bp'de değiştirilmesi gerekmiyor. Alan, *-update- *-update-api veya *-freeze-api eklenir veya güncellenir. Ayrıca, bir kullanıcı *-update- *-update-api veya *-freeze-api versions_with_info versions alanları otomatik olarak version_with_info'ya taşınır.
  • stability : Bu arayüzün kararlılık taahhüdü için isteğe bağlı bayrak. Şu anda yalnızca "vintf" desteklemektedir. Bu ayarlanmazsa, bu derleme bağlamı içinde kararlı bir arabirime karşılık gelir (bu nedenle buraya yüklenen bir arabirim yalnızca birlikte derlenen şeylerle kullanılabilir, örneğin system.img'de). Bu, "vintf" olarak ayarlanırsa, bu bir kararlılık taahhüdüne karşılık gelir: arabirim, kullanıldığı sürece sabit tutulmalıdır.
  • gen_trace : İzlemeyi açmak veya kapatmak için isteğe bağlı bayrak. Varsayılan false .
  • host_supported : true olarak ayarlandığında, oluşturulan kitaplıkları ana bilgisayar ortamı için kullanılabilir kılan isteğe bağlı bayrak.
  • unstable : Bu arabirimin kararlı olması gerekmediğini belirtmek için kullanılan isteğe bağlı bayrak. Bu, true olarak ayarlandığında, derleme sistemi arayüz için API dökümü oluşturmaz ve güncellenmesini gerektirmez.
  • backend.<type>.enabled : Bu bayraklar, AIDL derleyicisinin kod oluşturduğu arka uçların her birini değiştirir. Şu anda dört arka uç desteklenmektedir: Java, C++, NDK ve Rust. Java, C++ ve NDK arka uçları varsayılan olarak etkindir. Bu üç arka uçtan herhangi birine ihtiyaç yoksa, açıkça devre dışı bırakılması gerekir. Rust varsayılan olarak devre dışıdır.
  • backend.<type>.apex_available : Oluşturulan saplama kitaplığının kullanılabilir olduğu APEX adlarının listesi.
  • backend.[cpp|java].gen_log : İşlem hakkında bilgi toplamak için ek kod oluşturulup oluşturulmayacağını kontrol eden isteğe bağlı bayrak.
  • backend.[cpp|java].vndk.enabled : Bu arabirimi VNDK'nın bir parçası yapmak için isteğe bağlı bayrak. Varsayılan false .
  • backend.java.platform_apis : Java saplama kitaplığının platformdaki özel API'lere karşı oluşturulup oluşturulmadığını denetleyen isteğe bağlı bayrak. stability "vintf" olarak ayarlandığında bu "true" olarak ayarlanmalıdır.
  • backend.java.sdk_version : Java saplama kitaplığının dayandığı SDK sürümünü belirtmek için isteğe bağlı bayrak. Varsayılan "system_current" . Bu, backend.java.platform_apis doğru olduğunda ayarlanmamalıdır.
  • backend.java.platform_apis : Oluşturulan kitaplıkların SDK yerine platform API'sine göre oluşturulması gerektiğinde true olarak ayarlanması gereken isteğe bağlı bayrak.

Sürümlerin ve etkinleştirilmiş arka uçların her bir kombinasyonu için bir saplama kitaplığı oluşturulur. Belirli bir arka uç için saplama kitaplığının belirli sürümüne nasıl başvurulacağını öğrenmek için bkz. Modül adlandırma kuralları .

AIDL dosyalarını yazma

Kararlı AIDL'deki arabirimler, yapılandırılmamış parsellenebilirleri kullanmalarına izin verilmemesi dışında (çünkü bunlar kararlı değildir!) geleneksel arabirimlere benzer. Kararlı AIDL'deki birincil fark, parsellenebilirlerin nasıl tanımlandığıdır. Daha önce, parseller ileriye dönük olarak beyan ediliyordu; kararlı AIDL'de, parsellenebilir alanlar ve değişkenler açıkça tanımlanır.

// in a file like 'some/package/Thing.aidl'
package some.package;

parcelable SubThing {
    String a = "foo";
    int b;
}

Şu anda boolean , char , float , double , byte , int , long ve String için bir varsayılan desteklenmektedir (ancak gerekli değildir). Android 12'de, kullanıcı tanımlı numaralandırmalar için varsayılanlar da desteklenmektedir. Bir varsayılan belirtilmediğinde, 0 benzeri veya boş bir değer kullanılır. Varsayılan değeri olmayan numaralandırmalar, sıfır numaralandırıcı olmasa bile 0 olarak başlatılır.

Saplama kitaplıklarını kullanma

Saplama kitaplıklarını modülünüze bağımlılık olarak ekledikten sonra bunları dosyalarınıza dahil edebilirsiniz. Derleme sistemindeki saplama kitaplıklarının örnekleri aşağıda verilmiştir ( Android.mk , eski modül tanımları için de kullanılabilir):

cc_... {
    name: ...,
    shared_libs: ["my-module-name-cpp"],
    ...
}
# or
java_... {
    name: ...,
    // can also be shared_libs if desire is to load a library and share
    // it among multiple users or if you only need access to constants
    static_libs: ["my-module-name-java"],
    ...
}
# or
rust_... {
    name: ...,
    rust_libs: ["my-module-name-rust"],
    ...
}

C++'da örnek:

#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
    // use just like traditional AIDL

Java'da örnek:

import some.package.IFoo;
import some.package.Thing;
...
    // use just like traditional AIDL

Rust'ta Örnek:

use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
    // use just like traditional AIDL

Sürüm arayüzleri

Foo adlı bir modülün bildirilmesi, derleme sisteminde modülün API'sini yönetmek için kullanabileceğiniz bir hedef de oluşturur. foo-freeze-api oluşturulduğunda, Android sürümüne bağlı olarak api_dir veya aidl_api/ name altına yeni bir API tanımı ekler ve her ikisi de arayüzün yeni dondurulmuş sürümünü temsil eden bir .hash dosyası ekler. foo-freeze-api ayrıca, ek sürümü ve sürüm için imports aktarmaları yansıtacak şekilde versions_with_info özelliğini günceller. Temel olarak, imports versions_with_info imports . Ancak en son kararlı sürüm, açık bir versions_with_info olmayan içe aktarma için version_with_info'daki imports aktarmalarda belirtilir. versions_with_info özelliği belirtildikten sonra, yapı sistemi donmuş sürümler arasında ve ayrıca Ağacın Tepesi (ToT) ile en son donmuş sürüm arasında uyumluluk kontrolleri gerçekleştirir.

Ek olarak, ToT sürümünün API tanımını yönetmeniz gerekir. Bir API her güncellendiğinde, ToT sürümünün API tanımını içeren aidl_api/ name /current güncellemek için foo-update- api'yi çalıştırın.

Bir arabirimin kararlılığını korumak için sahipler yenilerini ekleyebilir:

  • Bir arabirimin sonuna kadar olan yöntemler (veya açıkça tanımlanmış yeni serilere sahip yöntemler)
  • Bir parsellenebilirin sonuna kadar olan öğeler (her öğe için bir varsayılanın eklenmesini gerektirir)
  • sabit değerler
  • Android 11'de numaralandırıcılar
  • Android 12'de, bir birliğin sonuna kadar olan alanlar

Başka hiçbir eyleme izin verilmez ve başka hiç kimse bir arabirimi değiştiremez (aksi takdirde, sahibin yaptığı değişikliklerle çakışma riskiyle karşı karşıya kalırlar).

Tüm arayüzlerin serbest bırakılmak üzere dondurulduğunu test etmek için, aşağıdaki çevresel değişkenleri ayarlayarak oluşturabilirsiniz:

  • AIDL_FROZEN_REL=true m ... - yapı, owner: alanı.
  • AIDL_FROZEN_OWNERS="aosp test" - derleme, tüm kararlı AIDL arabirimlerinin sahiple dondurulmasını gerektirir owner: "aosp" veya "test" olarak belirtilen alan.

İthalatın istikrarı

Bir arabirimin donmuş sürümleri için içe aktarma sürümlerinin güncellenmesi, Kararlı AIDL katmanında geriye doğru uyumludur. Ancak bunların güncellenmesi, arayüzün eski sürümünü kullanan tüm sunucuların ve istemcilerin güncellenmesini gerektirir ve bazı uygulamalar, farklı tür sürümlerini karıştırırken karıştırılabilir. Genel olarak, yalnızca türler veya ortak paketler için bu güvenlidir, çünkü IPC işlemlerinden bilinmeyen türleri işlemek için kodun zaten yazılması gerekir.

Android platform kodunda android.hardware.graphics.common , bu tür sürüm yükseltmenin en büyük örneğidir.

Sürümlü arayüzleri kullanma

Arayüz yöntemleri

Çalışma zamanında, eski bir sunucuda yeni yöntemler çağırmaya çalışırken, yeni istemciler arka uca bağlı olarak bir hata veya istisna alır.

  • cpp arka ucu ::android::UNKNOWN_TRANSACTION alır.
  • ndk arka ucu STATUS_UNKNOWN_TRANSACTION alır.
  • java arka ucu, API'nin uygulanmadığını belirten bir mesajla android.os.RemoteException alır.

Bununla başa çıkma stratejileri için bkz. sürümleri sorgulama ve varsayılanları kullanma .

parseller

Parsellere yeni alanlar eklendiğinde, eski istemciler ve sunucular bunları bırakır. Yeni istemciler ve sunucular eski parsellenebilirleri aldığında, yeni alanlar için varsayılan değerler otomatik olarak doldurulur. Bu, bir parsellenebilirdeki tüm yeni alanlar için varsayılanların belirtilmesi gerektiği anlamına gelir.

İstemciler, sunucunun tanımlanmış alana sahip sürümü uyguladığını bilmedikleri sürece sunucuların yeni alanları kullanmasını beklememelidir ( sürümleri sorgulamaya bakın).

Numaralandırmalar ve sabitler

Benzer şekilde, istemciler ve sunucular, gelecekte daha fazlası eklenebileceğinden, tanınmayan sabit değerleri ve numaralandırıcıları uygun şekilde reddetmeli veya yok saymalıdır. Örneğin, bir sunucu bilmediği bir numaralandırıcı aldığında iptal etmemelidir. Ya görmezden gelmeli ya da müşterinin bu uygulamada desteklenmediğini bilmesi için bir şey döndürmeli.

sendikalar

Alıcı eskiyse ve alan hakkında bilgisi yoksa, yeni bir alanla bir birleşim göndermeye çalışmak başarısız olur. Uygulama, yeni alanla birliği asla görmeyecektir. Tek yönlü bir işlem ise başarısızlık yok sayılır; aksi halde hata BAD_VALUE (C++ veya NDK arka ucu için) veya IllegalArgumentException (Java arka ucu için) olur. İstemci yeni alana bir birleşim kümesini eski bir sunucuya gönderiyorsa veya yeni bir sunucudan birliği alan eski bir istemci ise hata alınır.

Modül adlandırma kuralları

Android 11'de, etkinleştirilen sürümlerin ve arka uçların her kombinasyonu için otomatik olarak bir saplama kitaplığı modülü oluşturulur. Bağlanmak üzere belirli bir saplama kitaplığı modülüne başvurmak için aidl_interface modülünün adını değil, saplama kitaplığı modülünün adını kullanın; ifacename - version - backend burada

  • ifacename : aidl_interface modülünün adı
  • version aşağıdakilerden biridir
    • Dondurulmuş sürümler için V version-number
    • Ağacın ucu (henüz dondurulmamış) sürüm için V latest-frozen-version-number + 1
  • backend aşağıdakilerden biridir
    • Java arka ucu için java ,
    • C++ arka ucu için cpp ,
    • NDK arka ucu için ndk veya ndk_platform . İlki uygulamalar içindir ve ikincisi platform kullanımı içindir,
    • Rust arka ucu için rust .

foo adında bir modül olduğunu ve en son sürümünün 2 olduğunu ve hem NDK hem de C++'yı desteklediğini varsayalım. Bu durumda, AIDL şu modülleri üretir:

  • Versiyon 1'e göre
    • foo-V1-(java|cpp|ndk|ndk_platform|rust)
  • Sürüm 2'ye göre (en son kararlı sürüm)
    • foo-V2-(java|cpp|ndk|ndk_platform|rust)
  • ToT versiyonuna göre
    • foo-V3-(java|cpp|ndk|ndk_platform|rust)

Android 11 ile karşılaştırıldığında,

  • en son kararlı sürüme atıfta bulunan foo- backend , foo- V2 - backend olur
  • ToT sürümüne atıfta bulunan foo-unstable- backend , foo- V3 - backend olur

Çıktı dosyası adları her zaman modül adlarıyla aynıdır.

  • Sürüm 1'e göre: foo-V1-(cpp|ndk|ndk_platform|rust).so
  • Sürüm 2'ye göre: foo-V2-(cpp|ndk|ndk_platform|rust).so
  • ToT sürümüne göre: foo-V3-(cpp|ndk|ndk_platform|rust).so

AIDL derleyicisinin, kararlı bir AIDL arabirimi için unstable bir sürüm modülü veya sürüm bilgisi olmayan bir modül oluşturmadığını unutmayın. Android 12'den itibaren, kararlı bir AIDL arayüzünden oluşturulan modül adı her zaman sürümünü içerir.

Yeni meta arayüz yöntemleri

Android 10, kararlı AIDL için birkaç meta arayüz yöntemi ekler.

Uzak nesnenin arabirim sürümünü sorgulama

İstemciler, uzak nesnenin uyguladığı arabirimin sürümünü ve karmasını sorgulayabilir ve döndürülen değerleri, istemcinin kullandığı arabirimin değerleriyle karşılaştırabilir.

cpp arka ucu ile örnek:

sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();

ndk (ve ndk_platform ) arka ucu ile örnek:

IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);

java arka ucu ile örnek:

IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
  // the remote side is using an older interface
}

String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();

Java dili için, uzak taraf getInterfaceVersion() ve getInterfaceHash() aşağıdaki gibi UYGULAMALIDIR (kopyalama/yapıştırma hatalarını önlemek için IFoo yerine super kullanılır. @SuppressWarnings("static") ek açıklaması, bağlı olarak uyarıları devre dışı bırakmak için gerekli olabilir. javac yapılandırması):

class MyFoo extends IFoo.Stub {
    @Override
    public final int getInterfaceVersion() { return super.VERSION; }

    @Override
    public final String getInterfaceHash() { return super.HASH; }
}

Bunun nedeni, oluşturulan sınıfların ( IFoo , IFoo.Stub , vb.) istemci ve sunucu arasında paylaşılmasıdır (örneğin, sınıflar önyükleme sınıf yolunda olabilir). Sınıflar paylaşıldığında, arayüzün daha eski bir sürümüyle oluşturulmuş olsa bile sunucu, sınıfların en yeni sürümüyle de bağlantılıdır. Bu meta arabirim, paylaşılan sınıfta uygulanırsa, her zaman en yeni sürümü döndürür. Bununla birlikte, yukarıdaki yöntemi uygulayarak, arabirimin sürüm numarası sunucunun koduna gömülür (çünkü IFoo.VERSION , başvurulduğu zaman satır içine alınan static final int int'dir) ve bu nedenle yöntem, sunucunun oluşturulduğu tam sürümü döndürebilir ile birlikte.

Eski arayüzlerle ilgilenmek

Bir istemcinin bir AIDL arabiriminin daha yeni sürümüyle güncellenmesi, ancak sunucunun eski AIDL arabirimini kullanması mümkündür. Bu gibi durumlarda, eski bir arabirimde bir yöntemin çağrılması UNKNOWN_TRANSACTION döndürür.

İstikrarlı AIDL ile istemciler daha fazla kontrole sahip olur. İstemci tarafında, bir AIDL arabirimine varsayılan bir uygulama ayarlayabilirsiniz. Varsayılan uygulamadaki bir yöntem, yalnızca yöntem uzak tarafta uygulanmadığında çağrılır (çünkü arabirimin eski bir sürümüyle oluşturulmuştur). Varsayılanlar genel olarak ayarlandığından, potansiyel olarak paylaşılan bağlamlardan kullanılmamalıdır.

Android 13 ve sonrasında C++ örneği:

class MyDefault : public IFooDefault {
  Status anAddedMethod(...) {
   // do something default
  }
};

// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());

foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
                         // remote side is not implementing it

Java'da örnek:

IFoo.Stub.setDefaultImpl(new IFoo.Default() {
    @Override
    public xxx anAddedMethod(...)  throws RemoteException {
        // do something default
    }
}); // once per an interface in a process


foo.anAddedMethod(...);

Bir AIDL arabirimindeki tüm yöntemlerin varsayılan uygulamasını sağlamanız gerekmez. Uzak tarafta uygulanması garanti edilen yöntemlerin (çünkü uzaktan kumandanın, yöntemler AIDL arabirim açıklamasındayken oluşturulduğundan eminsiniz) varsayılan impl sınıfında geçersiz kılınması gerekmez.

Mevcut AIDL'yi yapılandırılmış/kararlı AIDL'ye dönüştürme

Mevcut bir AIDL arayüzünüz ve onu kullanan kodunuz varsa, arayüzü kararlı bir AIDL arayüzüne dönüştürmek için aşağıdaki adımları kullanın.

  1. Arayüzünüzün tüm bağımlılıklarını tanımlayın. Arayüzün bağlı olduğu her paket için, paketin kararlı AIDL'de tanımlanıp tanımlanmadığını belirleyin. Tanımlanmamışsa paket dönüştürülmelidir.

  2. Arayüzünüzdeki tüm ayrıştırılabilirleri kararlı ayrıştırılabilir parçalara dönüştürün (arayüz dosyalarının kendileri değişmeden kalabilir). Bunu, yapılarını doğrudan AIDL dosyalarında ifade ederek yapın. Bu yeni türleri kullanmak için yönetim sınıflarının yeniden yazılması gerekir. Bu, bir aidl_interface paketi oluşturmadan önce yapılabilir (aşağıda).

  3. Modülünüzün adını, bağımlılıklarını ve ihtiyaç duyduğunuz diğer bilgileri içeren bir aidl_interface paketi (yukarıda açıklandığı gibi) oluşturun. Dengeli hale getirmek için (sadece yapılandırılmış değil), aynı zamanda versiyonlanması gerekir. Daha fazla bilgi için bkz. Sürüm oluşturma arabirimleri .