Kararlı AIDL

Android 10, kararlı Android Arayüz Tanımlama Dili (AIDL) desteği ekler. Bu, AIDL arayüzleri tarafından sağlanan uygulama programlama arayüzünü (API) ve uygulama ikili arayüzünü (ABI) takip etmenin yeni bir yoludur. Kararlı AIDL, AIDL ile aynı şekilde çalışır ancak derleme sistemi arayüz uyumluluğunu izler ve yapabileceklerinizle ilgili kısıtlamalar vardır:

  • Arayüzler, derleme sisteminde aidl_interfaces ile tanımlanır.
  • Arayüzler yalnızca yapılandırılmış veri içerebilir. Tercih edilen türleri temsil eden Parcelable'lar, AIDL tanımlarına göre otomatik olarak oluşturulur ve otomatik olarak sıralanır ve sıralaması kaldırılır.
  • Arayüzler kararlı (geriye dönük uyumlu) olarak bildirilebilir. Bu durumda, API'leri AIDL arayüzünün yanındaki bir dosyada izlenir ve sürüm oluşturulur.

Yapılandırılmış ve kararlı AIDL

Yapılandırılmış AIDL, tamamen AIDL'de tanımlanan türleri ifade eder. Örneğin, paketlenebilir bir bildirim (özel bir paketlenebilir) yapılandırılmış AIDL değildir. Alanları AIDL'de tanımlanan Parcelable'lar yapılandırılmış Parcelable'lar olarak adlandırılır.

Kararlı AIDL, yapı sisteminin ve derleyicinin, paketlenebilir öğelerde yapılan değişikliklerin geriye dönük uyumlu olup olmadığını anlayabilmesi için yapılandırılmış AIDL gerektirir. Ancak tüm yapılandırılmış arayüzler kararlı değildir. Bir arayüzün kararlı olması için yalnızca yapılandırılmış türler kullanması ve aşağıdaki sürüm oluşturma özelliklerini kullanması gerekir. Aksine, arayüzü oluşturmak için temel derleme sistemi kullanılıyorsa veya unstable:true ayarlanmışsa arayüz kararlı değildir.

AIDL arayüzü tanımlama

aidl_interface tanımı şu şekildedir:

aidl_interface {
    name: "my-aidl",
    srcs: ["srcs/aidl/**/*.aidl"],
    local_include_dir: "srcs/aidl",
    imports: ["other-aidl"],
    versions_with_info: [
        {
            version: "1",
            imports: ["other-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 arayüzünü benzersiz şekilde tanımlayan AIDL arayüz modülünün adı.
  • srcs: Arayüzü oluşturan AIDL kaynak dosyalarının listesi. Bir pakette com.acme tanımlanan bir AIDL türü Foo için yol, <base_path>/com/acme/Foo.aidl olmalıdır. Burada <base_path>, Android.bp'nin bulunduğu dizinle ilgili herhangi bir dizin olabilir. Önceki örnekte <base_path>, srcs/aidl'tır.
  • local_include_dir: Paket adının başladığı yol. Yukarıda açıklanan <base_path> ile aynıdır.
  • imports: Bu modülün kullandığı aidl_interface modüllerin listesi. AIDL arayüzlerinizden biri başka bir aidl_interface arayüzü veya paketlenebilir öğesi kullanıyorsa adını buraya girin. En son sürümü belirtmek için yalnızca adı kullanabilir veya belirli bir sürümü belirtmek için sürüm sonekiyle birlikte (ör. -V1) adı kullanabilirsiniz. Sürüm belirtme, Android 12'den beri desteklenmektedir.
  • versions: Arayüzün api_dir altında dondurulmuş önceki sürümleri. Android 11'den itibaren versions, aidl_api/name altında dondurulur. Bir arayüzün dondurulmuş sürümü yoksa bu belirtilmemeli ve uyumluluk kontrolleri yapılmamalıdır. Bu alanın yerini Android 13 ve sonraki sürümlerde versions_with_info almıştır.
  • versions_with_info: Her biri dondurulmuş 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 sürüm içe aktarmalarını içeren bir liste olan demetlerin listesi. IFACE adlı bir AIDL arayüzünün V sürümünün tanımı aidl_api/IFACE/V konumunda bulunur. Bu alan Android 13'te kullanıma sunuldu ve doğrudan Android.bp içinde değiştirilmemesi gerekiyor. Alan, *-update-api veya *-freeze-api çağrılarak eklenir ya da güncellenir. Ayrıca, bir kullanıcı *-update-api veya *-freeze-api işlevini çağırdığında versions alanları otomatik olarak versions_with_info alanına taşınır.
  • stability: Bu arayüzün kararlılık sözü için isteğe bağlı işaret. Bu özellik yalnızca "vintf" destekler. stability ayarlanmamışsa derleme sistemi, unstable belirtilmediği sürece arayüzün geriye dönük uyumlu olduğunu kontrol eder. Ayarlanmamış olması, bu derleme bağlamında kararlılık içeren bir arayüze karşılık gelir (ör. system.img ve ilgili bölümlerdeki öğeler gibi tüm sistem öğeleri veya vendor.img ve ilgili bölümlerdeki öğeler gibi tüm satıcı öğeleri). stability, "vintf" olarak ayarlanırsa bu, kararlılık sözü anlamına gelir: Arayüz, kullanıldığı sürece kararlı tutulmalıdır.
  • gen_trace: İzlemeyi açmak veya kapatmak için isteğe bağlı işaret. Android 14'ten itibaren cpp ve java arka uçları için varsayılan değer true'dir.
  • host_supported: true olarak ayarlandığında oluşturulan kitaplıkları ana makine ortamında kullanılabilir hale getiren isteğe bağlı işaret.
  • unstable: Bu arayüzün kararlı olması gerekmediğini işaretlemek için kullanılan isteğe bağlı işaret. Bu true olarak ayarlandığında derleme sistemi, arayüz için API dökümü oluşturmaz ve güncellenmesini gerektirmez.
  • frozen: true olarak ayarlandığında arayüzün önceki sürümünden bu yana herhangi bir değişiklik yapılmadığını belirten isteğe bağlı işaret. Bu, daha fazla derleme zamanı kontrolü sağlar. false olarak ayarlandığında arayüzün geliştirme aşamasında olduğu ve yeni değişiklikler içerdiği anlamına gelir. Bu nedenle, foo-freeze-api komutunun çalıştırılması yeni bir sürüm oluşturur ve değeri otomatik olarak true olarak değiştirir. Android 14'te kullanıma sunuldu.
  • backend.<type>.enabled: Bu işaretler, AIDL derleyicisinin kod oluşturduğu her bir arka uç için açma/kapatma işlevi görür. Dört arka uç desteklenir: Java, C++, NDK ve Rust. Java, C++ ve NDK arka uçları varsayılan olarak etkindir. Bu üç arka uçtan herhangi birine ihtiyaç duyulmuyorsa açıkça devre dışı bırakılması gerekir. Rust, Android 15'e kadar varsayılan olarak devre dışıdır.
  • backend.<type>.apex_available: Oluşturulan saplama kitaplığının kullanılabildiği 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ı işaret.
  • backend.[cpp|java].vndk.enabled: Bu arayüzü VNDK'nın bir parçası yapmak için isteğe bağlı işaret. Varsayılan değer false'dır.
  • backend.[cpp|ndk].additional_shared_libraries: Android 14'te kullanıma sunulan bu işaret, yerel kitaplıklara bağımlılıklar ekler. Bu işaret, ndk_header ve cpp_header ile birlikte kullanıldığında işe yarar.
  • backend.java.sdk_version: Java saplama kitaplığının oluşturulduğu SDK sürümünü belirtmek için kullanılan isteğe bağlı işaret. Varsayılan değer: "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 göre oluşturulması gerektiğinde true olarak ayarlanması gereken isteğe bağlı işaret.

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

AIDL dosyaları yazma

Kararlı AIDL'deki arayüzler, yapılandırılmamış parcelable'lar kullanmalarına izin verilmemesi dışında geleneksel arayüzlere benzer (çünkü bunlar kararlı değildir. Yapılandırılmış ve kararlı AIDL bölümüne bakın). Kararlı AIDL'deki temel fark, paketlenebilirlerin nasıl tanımlandığıdır. Daha önce, parcelable'lar önceden bildirilmişti. Kararlı (ve dolayısıyla yapılandırılmış) AIDL'de ise parcelable alanları ve değişkenleri açıkça tanımlanır.

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

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

boolean, char, float, double, byte, int, long ve String için varsayılan değer desteklenir (ancak zorunlu değildir). Android 12'de, kullanıcı tanımlı numaralandırmalar için varsayılan değerler de desteklenir. Varsayılan değer belirtilmediğinde 0'a benzer 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.

Stub kitaplıkları kullanma

Sahte kitaplıkları modülünüze bağımlılık olarak ekledikten sonra bunları dosyalarınıza dahil edebilirsiniz. Aşağıda, derleme sistemindeki (Android.mk, eski modül tanımları için de kullanılabilir) stub kitaplıklarına ilişkin örnekler verilmiştir. Bu örneklerde sürüm bulunmadığı için kararsız bir arayüzün kullanıldığı gösterilmektedir. Sürümlü arayüzlerin adları ek bilgiler içerir. Arayüzleri sürümleme bölümüne bakın.

cc_... {
    name: ...,
    // use `shared_libs:` to load your library and its transitive dependencies
    // dynamically
    shared_libs: ["my-module-name-cpp"],
    // use `static_libs:` to include the library in this binary and drop
    // transitive dependencies
    static_libs: ["my-module-name-cpp"],
    ...
}
# or
java_... {
    name: ...,
    // use `static_libs:` to add all jars and classes to this jar
    static_libs: ["my-module-name-java"],
    // use `libs:` to make these classes available during build time, but
    // not add them to the jar, in case the classes are already present on the
    // boot classpath (such as if it's in framework.jar) or another jar.
    libs: ["my-module-name-java"],
    // use `srcs:` with `-java-sources` if you want to add classes in this
    // library jar directly, but you get transitive dependencies from
    // somewhere else, such as the boot classpath or another jar.
    srcs: ["my-module-name-java-source", ...],
    ...
}
# or
rust_... {
    name: ...,
    rustlibs: ["my-module-name-rust"],
    ...
}

C++'taki ö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'taki örnek:

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

Sürüm oluşturma 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. Oluşturulduğunda foo-freeze-api, Android sürümüne bağlı olarak api_dir veya aidl_api/name altında yeni bir API tanımı ekler ve arayüzün yeni dondurulmuş sürümünü temsil eden bir .hash dosyası ekler. foo-freeze-api, eklenen sürümü yansıtmak için versions_with_info özelliğini ve sürüm için imports özelliğini de günceller. Temel olarak, versions_with_info içindeki imports, imports alanından kopyalanır. Ancak içe aktarma için imports içinde versions_with_info'de en son kararlı sürüm belirtiliyor. Bu sürümün açık bir sürümü yok. versions_with_info özelliği belirtildikten sonra derleme sistemi, dondurulmuş sürümler arasında ve ayrıca Top of Tree (ToT) ile en son dondurulmuş sürüm arasında uyumluluk kontrolleri gerçekleştirir.

Ayrıca ToT sürümünün API tanımını da yönetmeniz gerekir. Bir API her güncellendiğinde, foo-update-api komutunu çalıştırarak aidl_api/name/current dosyasını güncelleyin. Bu dosya, ToT sürümünün API tanımını içerir.

Sahipler, arayüzün kararlılığını korumak için yeni öğeler ekleyebilir:

  • Bir arayüzün sonuna yönelik yöntemler (veya açıkça tanımlanmış yeni serileri olan yöntemler)
  • Bir paketlenebilir öğenin sonuna öğeler (her öğe için varsayılan değer eklenmelidir)
  • Sabit değerler
  • Android 11'de numaralandırıcılar
  • Android 12'de, bir birliğin sonundaki alanlar

Başka hiçbir işleme izin verilmez ve başka hiç kimse arayüzü değiştiremez (aksi takdirde, sahibin yaptığı değişikliklerle çakışma riski oluşur).

Tüm arayüzlerin yayın için dondurulduğunu test etmek amacıyla aşağıdaki ortam değişkenleri ayarlanmış şekilde derleme yapabilirsiniz:

  • AIDL_FROZEN_REL=true m ... - Derleme, owner: alanı belirtilmemiş tüm kararlı AIDL arayüzlerinin dondurulmasını gerektirir.
  • AIDL_FROZEN_OWNERS="aosp test" - Derleme, owner: alanı "aosp" veya "test" olarak belirtilmiş tüm kararlı AIDL arayüzlerinin dondurulmasını gerektirir.

İçe aktarma işlemlerinin kararlılığı

Bir arayüzün dondurulmuş sürümleri için içe aktarma sürümlerini güncelleme, Stable AIDL katmanında geriye dönük uyumludur. Ancak bunların güncellenmesi için arayüzün önceki bir sürümünü kullanan tüm sunucuların ve istemcilerin güncellenmesi gerekir. Ayrıca, farklı türlerin farklı sürümleri karıştırıldığında bazı uygulamalar karışabilir. Genellikle, yalnızca türleri içeren veya ortak paketler için bu güvenlidir. Bunun nedeni, IPC işlemlerinden gelen bilinmeyen türleri işlemek için kodun zaten yazılmış olması gerekmesidir.

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

Sürümlendirilmiş arayüzleri kullanma

Arayüz yöntemleri

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

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

Bunu ele alma stratejileri için sorgu sürümleri ve varsayılanları kullanma başlıklı makalelere bakın.

Ayrıştırılabilir öğeler

Parcelable'lara yeni alanlar eklendiğinde eski istemciler ve sunucular bunları bırakır. Yeni istemciler ve sunucular eski parcelable'ları aldığında yeni alanların varsayılan değerleri otomatik olarak doldurulur. Bu nedenle, bir paketlenebilir öğedeki tüm yeni alanlar için varsayılan değerler belirtilmesi gerekir.

İstemciler, sunucunun alanı tanımlanmış sürümü uyguladığını bilmedikleri sürece sunucuların yeni alanları kullanmasını beklememelidir (bkz. sürüm sorgulama).

Numaralandırmalar ve sabitler

Benzer şekilde, gelecekte daha fazla değer eklenebileceğinden, istemciler ve sunucular 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 işlemi durdurmamalıdır. Sunucu, numaralandırıcıyı yoksaymalı veya istemcinin bu uygulamada desteklenmediğini bilmesi için bir şey döndürmelidir.

Sendikalar

Alıcı eski bir sürümü kullanıyorsa ve alanı bilmiyorsa yeni bir alan içeren bir birliği gönderme işlemi başarısız olur. Uygulama, yeni alanla birleşmeyi hiçbir zaman görmez. Tek yönlü bir işlemse hata yoksayılır. Aksi takdirde hata BAD_VALUE(C++ veya NDK arka ucu için) ya da IllegalArgumentException(Java arka ucu için) olur. Hata, istemci eski bir sunucuya yeni alana bir birleşim kümesi gönderdiğinde veya yeni bir sunucudan birleşim alan eski bir istemci olduğunda alınır.

Birden fazla sürümü yönetme

Android'deki bir bağlayıcı ad alanı, oluşturulan aidl türlerinin birden fazla tanıma sahip olduğu durumları önlemek için belirli bir aidl arayüzünün yalnızca 1 sürümüne sahip olabilir. C++, her sembolün yalnızca bir tanımını gerektiren Tek Tanım Kuralı'na sahiptir.

Android derlemesi, bir modül aynı aidl_interface kitaplığının farklı sürümlerine bağlı olduğunda hata verir. Modül, bu kitaplıklara doğrudan veya bağımlılıklarının bağımlılıkları aracılığıyla dolaylı olarak bağlı olabilir. Bu hatalar, başarısız olan modülden aidl_interface kitaplığının çakışan sürümlerine kadar olan bağımlılık grafiğini gösterir. Tüm bağımlılıkların, bu kitaplıkların aynı (genellikle en son) sürümünü içerecek şekilde güncellenmesi gerekir.

Arayüz kitaplığı birçok farklı modül tarafından kullanılıyorsa aynı sürümü kullanması gereken kitaplık ve işlem grupları için cc_defaults, java_defaults ve rust_defaults oluşturmak faydalı olabilir. Arayüzün yeni bir sürümü kullanıma sunulduğunda bu varsayılanlar güncellenebilir ve bunları kullanan tüm modüller birlikte güncellenerek arayüzün farklı sürümlerinin kullanılmaması sağlanır.

cc_defaults {
  name: "my.aidl.my-process-group-ndk-shared",
  shared_libs: ["my.aidl-V3-ndk"],
  ...
}

cc_library {
  name: "foo",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

cc_binary {
  name: "bar",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

aidl_interface modülleri diğer aidl_interface modüllerini içe aktardığında, belirli sürümlerin birlikte kullanılmasını gerektiren ek bağımlılıklar oluşur. Bu durum, aynı işlemlerde birlikte kullanılan birden fazla aidl_interface modülüne ortak aidl_interface modüller içe aktarıldığında yönetilmesi zor bir hâl alabilir.

aidl_interfaces_defaults, tek bir yerde güncellenebilen ve bu ortak arayüzü içe aktarmak isteyen tüm aidl_interface modülleri tarafından kullanılabilen bir aidl_interface için bağımlılıkların en son sürümlerinin tek bir tanımını tutmak üzere kullanılabilir.

aidl_interface_defaults {
  name: "android.popular.common-latest-defaults",
  imports: ["android.popular.common-V3"],
  ...
}

aidl_interface {
  name: "android.foo",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

aidl_interface {
  name: "android.bar",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

İşaret tabanlı geliştirme

Geriye dönük uyumlulukları garanti edilmediği için geliştirme aşamasındaki (dondurulmamış) arayüzler, yayın cihazlarında kullanılamaz.

AIDL, kodun en son dondurulmamış sürüme göre yazılması ve yayınlanan cihazlarda kullanılmaya devam etmesi için bu dondurulmamış arayüz kitaplıklarında çalışma zamanı geri dönüşünü destekler. İstemcilerin geriye dönük uyumlu davranışı, mevcut davranışa benzer ve geri dönüşle birlikte uygulamaların da bu davranışları izlemesi gerekir. Sürümlendirilmiş arayüzleri kullanma başlıklı makaleyi inceleyin.

AIDL derleme işareti

Bu davranışı kontrol eden işaret, RELEASE_AIDL_USE_UNFROZEN build/release/build_flags.bzl içinde tanımlanır. true, arayüzün dondurulmamış sürümünün çalışma zamanında kullanıldığı, false ise dondurulmamış sürümlerin kitaplıklarının son dondurulmuş sürümleri gibi davrandığı anlamına gelir. Yerel geliştirme için işareti true olarak değiştirebilirsiniz ancak yayınlamadan önce false olarak geri döndürmeniz gerekir. Genellikle geliştirme, işaretin true olarak ayarlandığı bir yapılandırmayla yapılır.

Uyumluluk matrisi ve manifestler

Tedarikçi arayüzü nesneleri (VINTF nesneleri), hangi sürümlerin beklendiğini ve tedarikçi arayüzünün her iki tarafında hangi sürümlerin sağlandığını tanımlar.

Cuttlefish olmayan çoğu cihaz, yalnızca arayüzler dondurulduktan sonra en son uyumluluk matrisini hedefler. Bu nedenle, RELEASE_AIDL_USE_UNFROZEN'ye göre AIDL kitaplıklarında herhangi bir fark yoktur.

Matrisler

İş ortağına ait arayüzler, geliştirme sırasında cihazın hedeflediği cihaza özel veya ürüne özel uyumluluk matrislerine eklenir. Bu nedenle, bir uyumluluk matrisine yeni ve dondurulmamış bir arayüz sürümü eklendiğinde, önceki dondurulmuş sürümlerin RELEASE_AIDL_USE_UNFROZEN=false boyunca kalması gerekir. Bunu, farklı RELEASE_AIDL_USE_UNFROZEN yapılandırmaları için farklı uyumluluk matrisi dosyaları kullanarak veya tüm yapılandırmalarda kullanılan tek bir uyumluluk matrisi dosyasında her iki sürüme de izin vererek çözebilirsiniz.

Örneğin, dondurulmamış bir 4. sürüm eklerken <version>3-4</version> kullanın.

4. sürüm dondurulduğunda, RELEASE_AIDL_USE_UNFROZEN false olduğunda dondurulmuş 4. sürüm kullanıldığından 3. sürümü uyumluluk matrisinden kaldırabilirsiniz.

Manifestler

Android 15'te, libvintf değerine göre derleme sırasında manifest dosyalarını değiştirmek için RELEASE_AIDL_USE_UNFROZEN ile ilgili bir değişiklik yapıldı.

Manifestler ve manifest parçaları, bir hizmetin hangi arayüz sürümünü uyguladığını bildirir. Bir arayüzün dondurulmamış en son sürümü kullanılırken manifest, bu yeni sürümü yansıtacak şekilde güncellenmelidir. Manifest girişleri, oluşturulan AIDL kitaplığındaki değişikliği yansıtacak şekilde RELEASE_AIDL_USE_UNFROZEN=false tarafından ayarlanır.libvintf Sürüm, artık güncellenmeyen sürüm N'dan son dondurulmuş sürüm N - 1'e değiştirildi. Bu nedenle, kullanıcıların her hizmetleri için birden fazla manifest veya manifest parçası yönetmesi gerekmez.

HAL istemci değişiklikleri

HAL istemci kodu, desteklenen her önceki dondurulmuş sürümle geriye dönük olarak uyumlu olmalıdır. RELEASE_AIDL_USE_UNFROZEN false olduğunda hizmetler her zaman son dondurulmuş sürüm veya daha önceki bir sürüm gibi görünür (ör. yeni dondurulmamış yöntemleri çağırmak UNKNOWN_TRANSACTION değerini döndürür ya da yeni parcelable alanları varsayılan değerlerine sahiptir). Android çerçevesi istemcilerinin önceki sürümlerle geriye dönük uyumlu olması gerekir. Ancak bu, satıcı istemcileri ve iş ortağına ait arayüzlerin istemcileri için yeni bir ayrıntıdır.

HAL uygulamasında yapılan değişiklikler

İşaret tabanlı geliştirme ile HAL geliştirme arasındaki en büyük fark, RELEASE_AIDL_USE_UNFROZEN false olduğunda çalışmak için HAL uygulamalarının son dondurulmuş sürümle geriye dönük uyumlu olması gerekliliğidir. Uygulamalarda ve cihaz kodunda geriye dönük uyumluluğu göz önünde bulundurmak yeni bir uygulamadır. Sürümlendirilmiş arayüzleri kullanma başlıklı makaleyi inceleyin.

Geriye dönük uyumlulukla ilgili hususlar genellikle istemciler ve sunucular, çerçeve kodu ve satıcı kodu için aynıdır. Ancak artık aynı kaynak kodu (mevcut, dondurulmamış sürüm) kullanan iki sürümü etkili bir şekilde uyguladığınız için bilmeniz gereken küçük farklılıklar vardır.

Örnek: Bir arayüzün üç dondurulmuş sürümü var. Arayüz yeni bir yöntemle güncellenir. Hem istemci hem de hizmet, yeni 4. sürüm kitaplığını kullanacak şekilde güncellenir. V4 kitaplığı, arayüzün dondurulmamış bir sürümüne dayandığından RELEASE_AIDL_USE_UNFROZEN false olduğunda son dondurulmuş sürüm olan 3. sürüm gibi davranır ve yeni yöntemin kullanılmasını engeller.

Arayüz dondurulduğunda RELEASE_AIDL_USE_UNFROZEN değerlerinin tümü bu dondurulmuş sürümü kullanır ve geriye dönük uyumluluğu işleyen kod kaldırılabilir.

Geri aramalarda yöntemleri çağırırken UNKNOWN_TRANSACTION döndürüldüğünde durumu düzgün bir şekilde ele almanız gerekir. Müşteriler, yayın yapılandırmasına bağlı olarak geri çağırmanın iki farklı sürümünü uyguluyor olabilir. Bu nedenle, müşterinin en yeni sürümü gönderdiğini varsayamazsınız ve yeni yöntemler bunu döndürebilir. Bu, kararlı AIDL istemcilerinin Sürüm oluşturulmuş arayüzleri kullanma bölümünde açıklanan sunucularla geriye dönük uyumluluğu korumasına benzer.

// Get the callback along with the version of the callback
ScopedAStatus RegisterMyCallback(const std::shared_ptr<IMyCallback>& cb) override {
    mMyCallback = cb;
    // Get the version of the callback for later when we call methods on it
    auto status = mMyCallback->getInterfaceVersion(&mMyCallbackVersion);
    return status;
}

// Example of using the callback later
void NotifyCallbackLater() {
  // From the latest frozen version (V2)
  mMyCallback->foo();
  // Call this method from the unfrozen V3 only if the callback is at least V3
  if (mMyCallbackVersion >= 3) {
    mMyCallback->bar();
  }
}

Mevcut türlerdeki (parcelable, enum, union) yeni alanlar, RELEASE_AIDL_USE_UNFROZEN false olduğunda mevcut olmayabilir veya varsayılan değerlerini içerebilir. Ayrıca, bir hizmetin göndermeye çalıştığı yeni alanların değerleri, süreçten çıkarken bırakılır.

Bu sürümde eklenen yeni türler, arayüz üzerinden gönderilemez veya alınamaz.

RELEASE_AIDL_USE_UNFROZEN değeri false olduğunda uygulama, hiçbir istemciden yeni yöntemler için çağrı almaz.

Yeni numaralandırıcıları yalnızca kullanıma sunuldukları sürümde kullanmaya dikkat edin. Önceki sürümde kullanmayın.

Normalde, uzaktan arayüzün hangi sürümü kullandığını görmek için foo->getInterfaceVersion() kullanılır. Ancak işaret tabanlı sürüm oluşturma desteğiyle iki farklı sürüm uyguladığınız için mevcut arayüzün sürümünü almak isteyebilirsiniz. Bunu, mevcut nesnenin arayüz sürümünü (ör. this->getInterfaceVersion()) veya my_ver için diğer yöntemleri kullanarak yapabilirsiniz. Daha fazla bilgi için Uzak nesnenin arayüz sürümünü sorgulama başlıklı makaleyi inceleyin.

Yeni VINTF kararlı arayüzleri

Yeni bir AIDL arayüz paketi eklendiğinde son dondurulmuş sürüm olmadığından RELEASE_AIDL_USE_UNFROZEN false olduğunda geri dönülecek bir davranış yoktur. Bu arayüzleri kullanmayın. RELEASE_AIDL_USE_UNFROZEN değeri false olduğunda, Hizmet Yöneticisi hizmetin arayüzü kaydetmesine izin vermez ve istemciler arayüzü bulamaz.

Hizmetleri, cihazın makefile'ındaki RELEASE_AIDL_USE_UNFROZEN işaretinin değerine göre koşullu olarak ekleyebilirsiniz:

ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
    android.hardware.health.storage-service
endif

Hizmet daha büyük bir sürecin parçasıysa ve cihaza koşullu olarak eklenemiyorsa hizmetin IServiceManager::isDeclared() ile bildirilip bildirilmediğini kontrol edebilirsiniz. Bildirilmişse ve kaydı başarısız olduysa işlemi durdurun. Bildirilmezse kaydın başarısız olması beklenir.

Yeni VINTF kararlı uzantı arayüzleri

Yeni uzantı arayüzlerinin önceki bir sürümü yoktur ve ServiceManager ile kaydedilmedikleri veya VINTF manifestlerinde bildirilmedikleri için IServiceManager::isDeclared(), uzantı arayüzünün başka bir arayüze ne zaman ekleneceğini belirlemek için kullanılamaz.

RELEASE_AIDL_USE_UNFROZEN değişkeni, yeni dondurulmamış uzantı arayüzünün yayınlanan cihazlarda kullanılmasını önlemek için mevcut arayüze eklenip eklenmeyeceğini belirlemek üzere kullanılabilir. Arayüzün yayınlanan cihazlarda kullanılabilmesi için dondurulması gerekir.

vts_treble_vintf_vendor_test ve vts_treble_vintf_framework_test VTS testleri, yayınlanmış bir cihazda dondurulmamış bir uzantı arayüzü kullanıldığında bunu algılar ve hata verir.

Uzantı arayüzü yeni değilse ve daha önce dondurulmuş bir sürümü varsa bu dondurulmuş sürüme geri döner ve ek adım gerekmez.

Geliştirme aracı olarak Cuttlefish

VINTF dondurulduktan sonraki her yıl, çerçeve uyumluluk matrisini (FCM) target-level ve Cuttlefish'in PRODUCT_SHIPPING_API_LEVEL değerini, gelecek yılın sürümüyle kullanıma sunulan cihazları yansıtacak şekilde ayarlıyoruz. Gelecek yılki sürümün yeni şartlarını karşılayan ve test edilmiş bir başlatma cihazı olduğundan emin olmak için target-level ve PRODUCT_SHIPPING_API_LEVEL değerlerini ayarlıyoruz.

RELEASE_AIDL_USE_UNFROZEN, true olduğunda Cuttlefish, gelecekteki Android sürümlerinin geliştirilmesi için kullanılır. Bu, gelecek yılki Android sürümünün FCM düzeyini ve PRODUCT_SHIPPING_API_LEVEL'yı hedefler. Bu nedenle, gelecek sürümün Tedarikçi Yazılımı Şartları'nı (VSR) karşılaması gerekir.

RELEASE_AIDL_USE_UNFROZEN false olduğunda Cuttlefish, yayınlanan cihazı yansıtmak için önceki target-level ve PRODUCT_SHIPPING_API_LEVEL'ye sahiptir. Android 14 ve önceki sürümlerde bu farklılaştırma, FCM target-level, gönderim API seviyesi veya sonraki sürümü hedefleyen başka bir kodda yapılan değişikliği almayan farklı Git dallarıyla gerçekleştirilir.

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ğlama için belirli bir sahte kitaplık modülüne başvurmak üzere aidl_interface modülünün adını değil, sahte kitaplık modülünün adını kullanın. Bu ad, ifacename-version-backend biçimindedir.

  • ifacename: aidl_interface modülünün adı
  • version şunlardan biridir:
    • Artık güncellenmeyen sürümler için Vversion-number
    • Vlatest-frozen-version-number + 1, tip-of-tree (henüz dondurulmamış) sürümü için
  • backend şunlardan biridir:
    • Java arka ucu için java,
    • C++ arka ucu için cpp,
    • NDK arka ucu için ndk veya ndk_platform. Birincisi uygulamalar, ikincisi ise Android 13'e kadar platform kullanımı içindir. Android 13 ve sonraki sürümlerde yalnızca ndk simgesini kullanın.
    • Rust arka ucu için rust.

foo adlı bir modülün olduğunu ve bu modülün en son sürümünün 2 olduğunu varsayalım. Bu modül hem NDK'yı hem de C++'ı destekliyor. Bu durumda AIDL şu modülleri oluşturur:

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

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

  • foo-backend, en son kararlı sürümü ifade eden foo-V2-backend olur.
  • foo-unstable-backend, ToT sürümünü ifade eden foo-V3-backend olur.

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

  • 1. sürüme göre: foo-V1-(cpp|ndk|ndk_platform|rust).so
  • 2. sürüme 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 arayüzü için unstable sürüm modülü veya sürüm içermeyen 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 çeşitli meta arayüz yöntemleri ekler.

Uzak nesnenin arayüz sürümünü sorgulama

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

cpp arka ucuyla ilgili ö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 ucuyla ilgili ö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 dilinde, uzak taraf getInterfaceVersion() ve getInterfaceHash() işlevlerini aşağıdaki gibi uygulamalıdır (kopyalama ve yapıştırma hatalarını önlemek için IFoo yerine super kullanılır). @SuppressWarnings("static") yapılandırmasına bağlı olarak uyarıları devre dışı bırakmak için javac ek açıklaması gerekebilir:

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 eski bir sürümüyle oluşturulmuş olsa bile sunucu, sınıfların en yeni sürümüne göre de bağlanır. Bu meta arayüz, paylaşılan sınıfta uygulanırsa her zaman en yeni sürümü döndürür. Ancak yukarıdaki yöntemi uygulayarak arayüzün sürüm numarası sunucunun koduna yerleştirilir (çünkü IFoo.VERSION, referans verildiğinde satır içi yapılan bir static final int'dir). Bu nedenle, yöntem sunucunun oluşturulduğu tam sürümü döndürebilir.

Eski arayüzlerle çalışma

Bir istemci, AIDL arayüzünün daha yeni bir sürümüyle güncellenmiş olabilir ancak sunucu eski AIDL arayüzünü kullanıyor olabilir. Bu gibi durumlarda, eski bir arayüzde bir yöntemin çağrılması UNKNOWN_TRANSACTION değerini döndürür.

Kararlı AIDL ile istemciler daha fazla kontrol sahibi olur. İstemci tarafında, bir AIDL arayüzü için varsayılan uygulama ayarlayabilirsiniz. Varsayılan uygulamadaki bir yöntem yalnızca yöntem uzak tarafta uygulanmadığında (arayüzün eski bir sürümüyle oluşturulduğu için) çağrılır. Varsayılanlar genel olarak ayarlandığından, potansiyel olarak paylaşılan bağlamlardan kullanılmamalıdır.

Android 13 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 arayüzündeki tüm yöntemlerin varsayılan uygulamasını sağlamanız gerekmez. Uzak tarafta uygulanacağı kesin olan yöntemlerin (uzak tarafın, yöntemler AIDL arayüzü açıklamasındayken oluşturulduğundan eminseniz) varsayılan impl sınıfında geçersiz kılınması gerekmez.

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

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

  1. Arayüzünüzün tüm bağımlılıklarını belirleyin. 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 parcelable'ları kararlı parcelable'lara dönüştürün (arayüz dosyaları değişmeden kalabilir). Bunu, yapılarını doğrudan AIDL dosyalarında ifade ederek yapın. Yönetim sınıfları, bu yeni türleri kullanmak için yeniden yazılmalıdır. Bu işlem, aidl_interface paketi oluşturmadan önce (aşağıda) yapılabilir.

  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 oluşturun (yukarıda açıklandığı gibi). Dengeli (yalnızca yapılandırılmış değil) olması için sürüm oluşturulması da gerekir. Daha fazla bilgi için Arayüzlere sürüm oluşturma başlıklı makaleyi inceleyin.