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 arabirimini (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 ilan edilebilir. 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'lara yapılandırılmış Parcelable'lar denir.

Kararlı AIDL, derleme 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'dır.
  • local_include_dir: Paket adının başladığı yol. Bu, 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 parcelable kullanıyorsa adını buraya girin. En son sürümü belirtmek için yalnızca ad kullanılabilir veya belirli bir sürümü belirtmek için sürüm sonekiyle birlikte ad (ör. -V1) kullanılabilir. 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ümleri yoksa bu belirtilmemelidir ve uyumluluk kontrolleri yapılmaz. 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 sunulmuştur ve doğrudan Android.bp içinde değiştirilmemesi gerekir. 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 alanlarına taşınır.
  • stability: Bu arayüzün kararlılık sözü için isteğe bağlı işaret. Bu 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 etkinleştirmek veya devre dışı bırakmak için kullanılan 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 değer 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ığı anlamına gelen isteğe bağlı işaret. Bu, daha fazla derleme zamanı kontrolü yapılmasını 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 kaba kod 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". Bu, backend.java.platform_apis true 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ı 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 kaba kod kitaplığını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ış paketlenebilirler 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 ileri 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.

Kaba kod kitaplıklarını kullanma

Stub kitaplıklarını modülünüze bağımlılık olarak ekledikten sonra dosyalarınıza dahil edebilirsiniz. Aşağıda, derleme sistemindeki (Android.mk, eski modül tanımları için de kullanılabilir) sahte kitaplık örnekleri 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 dilinde ö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, ek 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şlemi için versions_with_info içindeki imports bölümünde en son kararlı sürüm belirtiliyor. Bu sürümde açık bir sürüm bilgisi 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.

Bir arayüzün kararlılığını korumak için sahipler yeni şunları ekleyebilir:

  • Bir arayüzün sonundaki yöntemler (veya açıkça tanımlanmış yeni serilere sahip yöntemler)
  • Bir paketlenebilir öğenin sonuna öğeler (her öğe için varsayılan değer eklenmesi gerekir)
  • Sabit değerler
  • Android 11'de numaralandırıcılar
  • Android 12'de bir birliğin sonuna kadar olan 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 üzere 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, tüm kararlı AIDL arayüzlerinin owner: alanı "aosp" veya "test" olarak belirtilerek dondurulmasını gerektirir.

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

Bir arayüzün dondurulmuş sürümlerine yönelik 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ür sürümleri karıştırıldığında bazı uygulamalar karışabilir. Genellikle yalnızca türleri veya ortak paketleri içeren durumlarda bu güvenlidir. Bunun nedeni, IPC işlemlerinden gelen bilinmeyen türleri işlemek için kodun önceden 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 yeni istemciler, arka uca bağlı olarak hata veya istisna alır.

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

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

Parcelables

Parcelable'lara yeni alanlar eklendiğinde eski istemciler ve sunucular bu alanları bırakır. Yeni istemciler ve sunucular eski paketlenebilirler 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 belirtilmelidir.

İ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. İstemci, birliği yeni alana eski bir sunucuya gönderiyorsa veya eski bir istemci, birliği yeni bir sunucudan alıyorsa hata alınır.

Modül düzeyinde sürüm başlığı (C++ / NDK)

cpp veya ndk arka uçlarını kullanan kararlı, sürüm oluşturulmuş AIDL modülleri için Soong, otomatik olarak modül düzeyinde yapay bir başlık oluşturur:

  • C++ arka ucu: #include "<module-name>.h"
  • NDK arka ucu: #include "aidl/<module-name>.h"

Bu başlık, modül adındaki noktalar AIDL_VERSION_<MODULE_NAME> ve tireler . ile tanımlanan - makrosunu büyük harfli alt çizgilere _ dönüştürür. Makroya, kullanılan kitaplığın sürümüne göre arayüz modülünün sürüm numarası atanır.

Örneğin, android.hardware.foo-V2-ndk makrosunu kullanabilirsiniz:

#include <aidl/android.hardware.foo.h>

#if AIDL_VERSION_ANDROID_HARDWARE_FOO >= 2
// Code for version 2 or higher
#endif

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 uyumlu olacağı garanti edilmediğinden, 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, 3. sürümü uyumluluk matrisinden kaldırabilirsiniz. Çünkü RELEASE_AIDL_USE_UNFROZEN olduğunda dondurulmuş 4. sürüm kullanılır. false.

Manifestler

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

Manifestler ve manifest parçaları, bir hizmetin hangi arayüz sürümünü uyguladığını belirtir. 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 hizmet 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

Bayrak tabanlı geliştirme ile HAL geliştirmedeki en büyük fark, RELEASE_AIDL_USE_UNFROZEN değeri 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 geriye dönük uyumluluğu göz önünde bulundurmak ve cihaz kodu 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ığı 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. İstemciler, yayın yapılandırmasına bağlı olarak geri çağırmanın iki farklı sürümünü uyguluyor olabilir. Bu nedenle, istemcinin en yeni sürümü gönderdiğini varsayamazsınız ve yeni yöntemler bunu döndürebilir. Bu, kararlı AIDL istemcilerinin Use versioned interfaces (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() simgesini kullanırsınız. 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 yoktur. Bu nedenle, RELEASE_AIDL_USE_UNFROZEN false olduğunda geri dönülecek bir davranış yoktur. Bu arayüzleri kullanmayın. RELEASE_AIDL_USE_UNFROZEN 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 ekleyemiyorsanız 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, gelecek yılın sürümüyle kullanıma sunulan cihazları yansıtacak şekilde ayarlarız. 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 hedefleyerek bir sonraki sürümün Tedarikçi Yazılımı Şartları'nı (VSR) karşılamasını gerektirir.

RELEASE_AIDL_USE_UNFROZEN false olduğunda Cuttlefish, yayınlanan bir cihazı yansıtmak için önceki target-level ve PRODUCT_SHIPPING_API_LEVEL değerlerine sahiptir. Android 14 ve önceki sürümlerde bu farklılaştırma, FCM'deki değişikliği target-level, gönderim API düzeyini veya sonraki sürümü hedefleyen başka bir kodu 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:
    • Dondurulmuş sürümler için Vversion-number
    • Vlatest-frozen-version-number + 1 for the tip-of-tree (yet-to-be-frozen) version
  • 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)
  • 2. sürüme (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 ediyordu. Bu sürüm foo-V3-backend olarak değiştirildi.

Çı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 istemcinin AIDL arayüzünün daha yeni bir sürümüyle güncellenmesi ancak sunucunun eski AIDL arayüzünü kullanması mümkündür. 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 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 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 oluşturun (yukarıda açıklandığı gibi). Dengelenmesi (yalnızca yapılandırılması değil) 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.