kararlı AIDL

Android 10, AIDL arabirimleri tarafından sağlanan uygulama programı arabirimini (API)/uygulama ikili arabirimini (ABI) izlemenin 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ış verileri 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.
  • Arayüzler 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ümlendirilir.

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: ["1", "2"],
    stability: "vintf",
    backend: {
        java: {
            enabled: true,
            platform_apis: true,
        },
        cpp: {
            enabled: true,
        },
        ndk: {
            enabled: true,
        },
    },

}
  • name : Bir AIDL arabirimini benzersiz olarak tanımlayan AIDL arabirim modülünün adı.
  • srcs : Arabirimi oluşturan AIDL kaynak dosyalarının listesi. com.acme paketinde tanımlanan Foo AIDL tipinin 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> ile uyumludur.
  • imports : Bunun kullandığı aidl_interface modüllerinin listesi. AIDL arayüzlerinizden biri bir arayüz veya başka bir aidl_interface ayrıştırılabilir bir arayüz 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 son ekine sahip ad ( -V1 gibi) olabilir. Sürüm belirtme Android 12'den beri desteklenmektedir
  • version : api_dir altında donmuş olan önceki versions , Android 11'den itibaren, versions aidl_api/ name altında dondurulur. Bir arayüzün donmuş sürümleri yoksa, bu belirtilmemelidir ve uyumluluk kontrolleri yapılmayacaktır.
  • stability : Bu arabirimin kararlılık vaadi için isteğe bağlı bayrak. Şu anda yalnızca "vintf" destekler. Bu ayarlanmamışsa, bu derleme bağlamında 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 üzerinde). Bu "vintf" olarak ayarlanırsa, bu bir kararlılık vaadine karşılık gelir: arayüz 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ında kullanılabilir hale getiren 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 arabirim için API dökümü oluşturmaz veya güncellenmesini gerektirmez.
  • backend.<type>.enabled : Bu bayraklar, AIDL derleyicisinin kod üreteceği arka uçların her birini değiştirir. Şu anda üç arka uç desteklenmektedir: java , cpp ve ndk . Arka uçların tümü varsayılan olarak etkindir. Belirli bir arka uca ihtiyaç duyulmadığında, açıkça devre dışı bırakılması gerekir.
  • 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ı kontrol eden 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 oluşturulduğu SDK sürümünü belirtmek için isteğe bağlı bayrak. Varsayılan "system_current" . backend.java.platform_apis true olduğunda bu ayarlanmamalıdır.
  • backend.java.platform_apis : Oluşturulan kitaplıkların SDK yerine platform API'sine karşı oluşturulması gerektiğinde true olarak ayarlanması gereken isteğe bağlı bayrak.

versions 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ğına ilişkin Modül adlandırma kurallarına bakın.

AIDL dosyalarının yazılması

Kararlı AIDL'deki arabirimler, yapılandırılmamış parsellenebilirleri kullanmalarına izin verilmemesi (çünkü bunlar kararlı değildir!) dışında geleneksel arabirimlere benzer. Kararlı AIDL'deki birincil fark, parsellenebilirlerin nasıl tanımlandığıdır. Daha önce parsellenebilirler ileriye dönük 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 desteklenir. 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

Modülünüze bağımlılık olarak saplama kitaplıkları ekledikten sonra, bunları dosyalarınıza dahil edebilirsiniz. İşte derleme sistemindeki saplama kitaplıklarının örnekleri ( 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"],
    ...
}

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

Sürüm oluşturma arayüzleri

Adı foo olan bir modül bildirmek, yapı 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 donmuş sürümünü temsil eden bir .hash dosyası ekler. Bunu oluşturmak, ek versions yansıtacak şekilde version özelliğini de günceller. Versions özelliği belirtildiğinde, yapı sistemi donmuş versions arasında ve ayrıca Ağacın Başı (ToT) ile en son donmuş sürüm arasında uyumluluk kontrolleri çalıştırır.

Ayrıca ToT sürümünün API tanımını da yönetmeniz gerekir. Bir API 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 arayüzün kararlılığını korumak için sahipler yenilerini ekleyebilir:

  • Bir arabirimin sonuna kadar yöntemler (veya açıkça tanımlanmış yeni dizilere sahip yöntemler)
  • Parçalanabilir bir öğenin 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 alanlar

Başka hiçbir eyleme izin verilmez ve başka hiç kimse bir arabirimi değiştiremez (aksi takdirde, bir sahibin yaptığı değişikliklerle çakışma riski vardır).

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 ya bir hata ya da bir 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ı söyleyen bir mesajla android.os.RemoteException alır.

Bunu ele alma stratejileri için sürümleri sorgulama ve varsayılanları kullanma konusuna bakın.

parsellenebilirler

Parçalanabilirlere 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, alan tanımlı olan sürümü uyguladığını bilmedikçe, sunucuların yeni alanları kullanmasını beklememelidir ( sorgulama sürümlerine 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 yoksaymalıdır. Örneğin, bir sunucu bilmediği bir numaralandırıcı aldığında iptal etmemelidir. Ya yok saymalı ya da müşterinin bu uygulamada desteklenmediğini bilmesi için bir şey döndürmelidir.

sendikalar

Alıcı eskiyse ve alanı bilmiyorsa, yeni bir alanla bir birlik göndermeye çalışmak başarısız olur. Uygulama, yeni alan ile birliği asla görmeyecektir. Tek yönlü bir işlemse, başarısızlık yoksayılır; aksi takdirde hata BAD_VALUE (C++ veya NDK arka ucu için) veya IllegalArgumentException (Java arka ucu için) olur. İstemci eski bir sunucuya yeni alana bir birleşim seti gönderiyorsa veya eski bir istemci birliği yeni bir sunucudan alıyorsa hata alınır.

Modül adlandırma kuralları

Android 11'de, sürümlerin ve etkinleştirilen arka uçların her bir kombinasyonu için otomatik olarak bir saplama kitaplığı modülü oluşturulur. Bağlama için 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ı, yani ifacename - version - backend kullanın, burada

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

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

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

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

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

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

  • Sürüm 1: foo-V1-(cpp|ndk|ndk_platform).so
  • 2. sürüme göre: foo-V2-(cpp|ndk|ndk_platform).so
  • ToT sürümüne dayalıdır: foo-V3-(cpp|ndk|ndk_platform).so

AIDL derleyicisinin, kararlı bir AIDL arabirimi için unstable bir sürüm modülü veya sürümsüz 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 arayüz versiyonunu 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 ucuyla ilgili ö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 uygulamalıdır:

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

    @Override
    public final String getInterfaceHash() { return IFoo.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, sunucu, arayüzün daha eski bir sürümüyle oluşturulmuş olsa bile, sınıfların en yeni sürümüne de bağlanır. Bu meta arabirim paylaşılan sınıfta uygulanırsa, her zaman en yeni sürümü döndürür. Ancak, yöntemi yukarıdaki gibi uygulayarak, arabirimin sürüm numarası sunucunun koduna gömülür (çünkü IFoo.VERSION , başvurulduğunda satır içi olan static final int int'dir) ve böylece yöntem, sunucunun tam olarak oluşturulduğu sürümü döndürebilir. ile.

Eski arayüzlerle uğraşmak

İstemcinin 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.

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

Android T (AOSP deneysel) ve sonraki sürümlerde 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 (yöntemler AIDL arabirim açıklamasındayken oluşturulduğundan emin olduğunuz için) 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ılabilirlere 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ı yeniden yazılmalıdır. 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 ihtiyacınız olan diğer bilgileri içeren bir aidl_interface paketi (yukarıda açıklandığı gibi) oluşturun. Bunu stabilize etmek için (sadece yapılandırılmış değil), aynı zamanda versiyonlanması gerekir. Daha fazla bilgi için Sürüm oluşturma arabirimlerine bakın.