安定版の AIDL

Android 10 では、安定した Android インターフェイス定義言語 (AIDL) のサポートが追加されています。これは、AIDL インターフェイスによって提供されるアプリケーション プログラム インターフェイス (API)/アプリケーション バイナリ インターフェイス (ABI) を追跡するための新しい方法です。安定版 AIDL には、AIDL と次のような重要な違いがあります。

  • インターフェイスは、 aidl_interfacesを使用してビルド システムで定義されます。
  • インターフェイスには構造化データのみを含めることができます。目的のタイプを表す Parcelable は、AIDL 定義に基づいて自動的に作成され、自動的にマーシャリングおよびアンマーシャリングされます。
  • インターフェイスは安定している (下位互換性がある) と宣言できます。これが発生すると、API は AIDL インターフェイスの隣のファイルで追跡され、バージョン管理されます。

構造化された AIDL と安定した AIDL

構造化 AIDL は、純粋に AIDL で定義された型を指します。たとえば、Parcelable 宣言 (カスタム Parcelable) は構造化 AIDL ではありません。 AIDL でフィールドが定義された Parcelable は、構造化 Parcelableと呼ばれます。

安定した AIDL には、Parcelable に加えられた変更に下位互換性があるかどうかをビルド システムとコンパイラーが理解できるように、構造化された AIDL が必要です。ただし、すべての構造化インターフェイスが安定しているわけではありません。安定させるには、インターフェイスは構造化型のみを使用する必要があり、また次のバージョン管理機能も使用する必要があります。逆に、インターフェイスのビルドにコア ビルド システムが使用されている場合や、 unstable:trueが設定されている場合、インターフェイスは安定していません。

AIDLインターフェースの定義

aidl_interfaceの定義は次のようになります。

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 : AIDL インターフェイスを一意に識別する AIDL インターフェイス モジュールの名前。
  • srcs : インターフェイスを構成する AIDL ソース ファイルのリスト。パッケージcom.acmeで定義されている AIDL タイプFooのパスは、 <base_path>/com/acme/Foo.aidlにある必要があります。 <base_path>は、 Android.bpがあるディレクトリに関連する任意のディレクトリです。上の例では、 <base_path>srcs/aidlです。
  • local_include_dir : パッケージ名の開始位置のパス。上で説明した<base_path>に相当します。
  • imports : これが使用するaidl_interfaceモジュールのリスト。 AIDL インターフェイスの 1 つが別のaidl_interfaceインターフェイスまたは Parcelable を使用する場合は、その名前をここに入力します。これは、最新バージョンを参照するために名前自体を使用することも、特定のバージョンを参照するためにバージョン接尾辞 ( -V1など) を付けた名前にすることもできます。バージョンの指定は Android 12 以降でサポートされています
  • versions : api_dirの下でフリーズされているインターフェイスの以前のバージョン。Android 11 以降、 versions aidl_api/ nameの下にフリーズされます。インターフェイスの凍結バージョンがない場合、これを指定すべきではなく、互換性チェックは行われません。 13 以降では、このフィールドはversions_with_infoに置き換えられました。
  • versions_with_info : タプルのリスト。各タプルには、凍結されたバージョンの名前と、このバージョンの aidl_interface がインポートした他の aidl_interface モジュールのバージョン インポートのリストが含まれます。 AIDL インターフェイス IFACE のバージョン V の定義はaidl_api/ IFACE / Vにあります。このフィールドは Android 13 で導入されたもので、Android.bp で直接変更することは想定されていません。フィールドは、 *-update-apiまたは*-freeze-apiを呼び出すことによって追加または更新されます。また、ユーザーが*-update-apiまたは*-freeze-apiを呼び出すと、 versionsフィールドは自動的にversions_with_infoに移行されます。
  • stability : このインターフェースの安定性を保証するためのオプションのフラグ。現在は"vintf"のみをサポートしています。これが設定されていない場合、これはこのコンパイル コンテキスト内で安定したインターフェイスに対応します (したがって、ここでロードされたインターフェイスは、一緒にコンパイルされたもの (たとえば system.img 上) でのみ使用できます)。これが"vintf"に設定されている場合、これは安定性の約束に相当します。インターフェイスは、使用されている限り安定した状態に保たれなければなりません。
  • gen_trace : トレースをオンまたはオフにするオプションのフラグ。 Android 14 以降、 cppおよびjavaバックエンドのデフォルトはtrueです。
  • host_supported : trueに設定すると、生成されたライブラリをホスト環境で使用できるようにするオプションのフラグ。
  • unstable : このインターフェースが安定している必要がないことを示すために使用されるオプションのフラグ。これがtrueに設定されている場合、ビルド システムはインターフェイスの API ダンプを作成せず、更新する必要もありません。
  • frozen : オプションのフラグ。 trueに設定すると、インターフェイスが前のバージョンのインターフェイスから変更されていないことを意味します。これにより、より多くのビルド時チェックが可能になります。 falseに設定すると、インターフェイスが開発中で新しい変更が加えられたことを意味するため、 foo-freeze-apiを実行すると新しいバージョンが生成され、値が自動的にtrueに変更されます。 Android 14で導入されました。
  • backend.<type>.enabled : これらのフラグは、AIDL コンパイラーがコードを生成する各バックエンドを切り替えます。現在、Java、C++、NDK、Rust の 4 つのバックエンドがサポートされています。 Java、C++、NDK バックエンドはデフォルトで有効になっています。これら 3 つのバックエンドのいずれかが不要な場合は、明示的に無効にする必要があります。 Rust はデフォルトでは無効になっています。
  • backend.<type>.apex_available : 生成されたスタブ ライブラリが使用できる APEX 名のリスト。
  • backend.[cpp|java].gen_log : トランザクションに関する情報を収集するための追加コードを生成するかどうかを制御するオプションのフラグ。
  • backend.[cpp|java].vndk.enabled : このインターフェイスを VNDK の一部にするためのオプションのフラグ。デフォルトはfalseです。
  • backend.[cpp|ndk].additional_shared_libraries : Android 14 で導入されたこのフラグは、ネイティブ ライブラリに依存関係を追加します。このフラグは、 ndk_headerおよびcpp_headerで使用すると便利です。
  • backend.java.sdk_version : Java スタブ ライブラリが構築される SDK のバージョンを指定するためのオプションのフラグ。デフォルトは"system_current"です。 backend.java.platform_apisが true の場合、これを設定しないでください。
  • backend.java.platform_apis : 生成されたライブラリを SDK ではなくプラットフォーム API に対してビルドする必要がある場合にtrueに設定する必要があるオプションのフラグ。

バージョンと有効なバックエンドの組み合わせごとに、スタブ ライブラリが作成されます。特定のバックエンドのスタブ ライブラリの特定のバージョンを参照する方法については、 「 モジュールの命名規則 」を参照してください。

AIDL ファイルの書き込み

安定版 AIDL のインターフェイスは従来のインターフェイスに似ていますが、非構造化 Parcelable の使用が許可されていない点が異なります (安定版 AIDL は安定していないためです。構造化 AIDL と安定版 AIDLを参照してください)。安定版 AIDL の主な違いは、Parcelable の定義方法です。以前は、Parcelable は前方宣言されていました。安定した (したがって構造化された) AIDL では、Parcelables フィールドと変数は明示的に定義されます。

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

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

現在、 booleancharfloatdoublebyteintlong 、およびStringのデフォルトがサポートされています (必須ではありません)。 Android 12 では、ユーザー定義の列挙のデフォルトもサポートされています。デフォルトが指定されていない場合は、0 に似た値または空の値が使用されます。デフォルト値のない列挙は、ゼロ列挙子がない場合でも 0 に初期化されます。

スタブライブラリの使用

スタブ ライブラリを依存関係としてモジュールに追加した後、それらをファイルに含めることができます。ビルド システムのスタブ ライブラリの例を次に示します ( Android.mkレガシー モジュール定義にも使用できます)。

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++ の例:

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

Java での例:

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

Rustでの例:

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

バージョン管理インターフェース

fooという名前のモジュールを宣言すると、モジュールの API の管理に使用できるターゲットがビルド システム内に作成されます。ビルドされると、 foo-freeze-api は、Android バージョンに応じてapi_dirまたはaidl_api/ nameの下に新しい API 定義を追加し、 .hashファイルを追加します。どちらもインターフェイスの新しく凍結されたバージョンを表します。 foo-freeze-api はversions_with_infoプロパティも更新して、追加のバージョンとそのバージョンのimportsを反映します。基本的に、 versions_with_infoimports importsフィールドからコピーされます。ただし、明示的なバージョンがないimportsの場合は、最新の安定バージョンがversions_with_infoのインポートで指定されます。 versions_with_infoプロパティが指定されると、ビルド システムは凍結されたバージョン間、およびトップ オブ ツリー (ToT) と最新の凍結されたバージョンの間の互換性チェックを実行します。

さらに、ToT バージョンの API 定義を管理する必要があります。 API が更新されるたびに、 foo-update-apiを実行して、ToT バージョンの API 定義を含むaidl_api/ name /currentを更新します。

インターフェイスの安定性を維持するために、所有者は新しいものを追加できます。

  • インターフェイスの最後までのメソッド (または明示的に定義された新しいシリアルを持つメソッド)
  • Parcelable の最後までの要素 (要素ごとにデフォルトを追加する必要があります)
  • 定数値
  • Android 11 では、列挙子
  • Android 12 では、フィールドからユニオンの末尾まで

他のアクションは許可されず、誰もインターフェイスを変更できません (そうしないと、所有者が行った変更と衝突する危険があります)。

すべてのインターフェイスがリリースのためにフリーズされていることをテストするには、次の環境変数を設定してビルドできます。

  • AIDL_FROZEN_REL=true m ... - ビルドでは、 owner:フィールドが指定されていないすべての安定した AIDL インターフェイスを凍結する必要があります。
  • AIDL_FROZEN_OWNERS="aosp test" - ビルドでは、すべての安定した AIDL インターフェイスが、「aosp」または「test」として指定されたowner:フィールドで凍結される必要があります。

輸入品の安定性

インターフェイスの凍結バージョンのインポートのバージョンの更新は、安定版 AIDL レイヤーで下位互換性があります。ただし、これらを更新するには、古いバージョンのインターフェイスを使用するすべてのサーバーとクライアントを更新する必要があり、一部のアプリケーションでは、異なるバージョンのタイプを混在させると混乱する可能性があります。一般に、タイプのみのパッケージまたは共通パッケージの場合、IPC トランザクションからの未知のタイプを処理するコードを事前に記述する必要があるため、これは安全です。

Android プラットフォーム コードのandroid.hardware.graphics.common 、このタイプのバージョン アップグレードの最大の例です。

バージョン管理されたインターフェースの使用

インターフェースメソッド

実行時に古いサーバーで新しいメソッドを呼び出そうとすると、新しいクライアントはバックエンドに応じてエラーまたは例外を受け取ります。

  • cppバックエンドは::android::UNKNOWN_TRANSACTIONを取得します。
  • ndkバックエンドはSTATUS_UNKNOWN_TRANSACTIONを取得します。
  • javaバックエンドは、API が実装されていないことを示すメッセージを含むandroid.os.RemoteExceptionを取得します。

これに対処する方法については、 「バージョンのクエリデフォルトの使用」を参照してください。

Parcelables

新しいフィールドが Parcelable に追加されると、古いクライアントとサーバーはそれらを削除します。新しいクライアントとサーバーが古い Parcelable を受信すると、新しいフィールドのデフォルト値が自動的に入力されます。これは、Parcelable 内のすべての新しいフィールドにデフォルトを指定する必要があることを意味します。

クライアントは、フィールドが定義されているバージョンをサーバーが実装していることを認識していない限り、サーバーが新しいフィールドを使用することを期待しないでください ( 「バージョンのクエリ」を参照)。

列挙型と定数

同様に、クライアントとサーバーは、認識できない定数値と列挙子が将来追加される可能性があるため、必要に応じて拒否または無視する必要があります。たとえば、サーバーは、未知の列挙子を受信したときに中断すべきではありません。それを無視するか、この実装ではサポートされていないことをクライアントに知らせるために何かを返す必要があります。

労働組合

受信側が古く、そのフィールドについて知らない場合、新しいフィールドを含む共用体を送信しようとすると失敗します。実装では新しいフィールドとの結合が見られることはありません。一方向トランザクションの場合、失敗は無視されます。それ以外の場合、エラーはBAD_VALUE (C++ または NDK バックエンドの場合) またはIllegalArgumentException (Java バックエンドの場合) になります。このエラーは、クライアントが新しいフィールドに設定された共用体を古いサーバーに送信している場合、または古いクライアントが新しいサーバーから共用体を受信して​​いる場合に受信されます。

フラグベースの開発

開発中 (凍結されていない) インターフェイスは、下位互換性が保証されていないため、リリース デバイスでは使用できません。

AIDL は、最新の凍結されていないバージョンに対してコードを記述し、リリース デバイスでも引き続き使用できるように、これらの凍結されていないインターフェイス ライブラリの実行時フォールバックをサポートしています。クライアントの下位互換性のある動作は既存の動作と似ており、フォールバックでは実装もそれらの動作に従う必要があります。 「バージョン管理されたインターフェイスの使用」を参照してください。

AIDL ビルド フラグ

この動作を制御するフラグはbuild/release/build_flags.bzlで定義されているRELEASE_AIDL_USE_UNFROZENです。 trueは、インターフェイスの凍結されていないバージョンが実行時に使用されることを意味し、 falseは、凍結されていないバージョンのライブラリがすべて、最後に凍結されたバージョンと同様に動作することを意味します。ローカル開発ではフラグをtrueにオーバーライドできますが、リリース前にfalseに戻す必要があります。通常、開発はフラグがtrueに設定された構成で行われます。

互換性マトリックスとマニフェスト

ベンダー インターフェイス オブジェクト (VINTF オブジェクト) は、予期されるバージョンと、ベンダー インターフェイスの両側で提供されるバージョンを定義します。

Cuttlefish 以外のほとんどのデバイスは、インターフェイスがフリーズされた後にのみ最新の互換性マトリックスをターゲットにするため、 RELEASE_AIDL_USE_UNFROZENに基づく AIDL ライブラリに違いはありません。

パートナー所有のインターフェイスは、開発中にデバイスがターゲットとするデバイス固有または製品固有の互換性マトリックスに追加されます。したがって、インターフェイスの新しい凍結されていないバージョンが互換性マトリックスに追加される場合、以前の凍結されたバージョンはRELEASE_AIDL_USE_UNFROZEN=falseのままにしておく必要があります。これに対処するには、異なるRELEASE_AIDL_USE_UNFROZEN構成ごとに異なる互換性マトリックス ファイルを使用するか、すべての構成で使用される単一の互換性マトリックス ファイルで両方のバージョンを許可します。

たとえば、凍結されていないバージョン 4 を追加する場合は、 <version>3-4</version>を使用します。

バージョン 4 がフリーズされている場合、 RELEASE_AIDL_USE_UNFROZENfalseの場合にフリーズされたバージョン 4 が使用されるため、互換性マトリックスからバージョン 3 を削除できます。

HAL クライアントの変更

HAL クライアント コードは、以前にサポートされていた各凍結バージョンと下位互換性がある必要があります。 RELEASE_AIDL_USE_UNFROZENfalseの場合、サービスは常に最後に凍結されたバージョン以前と同じように見えます (たとえば、新しい解凍されたメソッドを呼び出すとUNKNOWN_TRANSACTIONが返されるか、新しいparcelableフィールドがデフォルト値になります)。 Android フレームワーク クライアントは、追加の前バージョンとの下位互換性を維持する必要がありますが、これはベンダー クライアントおよびパートナー所有のインターフェイスのクライアントにとっては新しい詳細です。

HAL 実装の変更

HAL 開発とフラグベースの開発の最大の違いは、 RELEASE_AIDL_USE_UNFROZEN falseの場合に動作するために、HAL 実装が最​​後に凍結されたバージョンと下位互換性を持つ必要があることです。実装とデバイス コードにおける下位互換性を考慮することは、新たな課題です。 「バージョン管理されたインターフェイスの使用」を参照してください。

下位互換性に関する考慮事項は、クライアントとサーバー、およびフレームワーク コードとベンダー コードについては通常同じですが、同じソース コードを使用する 2 つのバージョンを効果的に実装することになるため、注意が必要な微妙な違いがあります。 (現在の、凍結されていないバージョン)。

例: インターフェイスには 3 つの凍結バージョンがあります。インターフェースが新しいメソッドで更新されました。クライアントとサービスは両方とも、新しいバージョン 4 ライブラリを使用するように更新されます。 V4 ライブラリはインターフェイスの凍結されていないバージョンに基づいているため、 RELEASE_AIDL_USE_UNFROZENfalseの場合、最後の凍結バージョンであるバージョン 3 と同様に動作し、新しいメソッドの使用が妨げられます。

インターフェイスがフリーズされると、 RELEASE_AIDL_USE_UNFROZENのすべての値がそのフリーズされたバージョンを使用し、下位互換性を処理するコードを削除できます。

コールバックでメソッドを呼び出すときは、 UNKNOWN_TRANSACTION返された場合に適切に処理する必要があります。クライアントはリリース構成に基づいて 2 つの異なるバージョンのコールバックを実装している可能性があるため、クライアントが最新バージョンを送信するとは想定できず、新しいメソッドがこれを返す可能性があります。これは、 「バージョン管理されたインターフェイスの使用」で説明されている、安定した AIDL クライアントがサーバーとの下位互換性を維持する方法と似ています。

// 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();
  }
}

RELEASE_AIDL_USE_UNFROZENfalse 、サービスが送信しようとする新しいフィールドの値がプロセスの途中でドロップされる場合、既存の型 ( parcelableenumunion ) の新しいフィールドが存在しないか、デフォルト値が含まれている可能性があります。

この非凍結バージョンに追加された新しいタイプは、インターフェイスを介して送受信できません。

RELEASE_AIDL_USE_UNFROZENfalseの場合、実装はクライアントから新しいメソッドの呼び出しを取得しません。

新しい列挙子は、以前のバージョンではなく、導入されたバージョンでのみ使用するように注意してください。

通常、リモート インターフェイスが使用しているバージョンを確認するには、 foo->getInterfaceVersion()を使用します。ただし、フラグベースのバージョン管理サポートを使用すると、2 つの異なるバージョンを実装することになるため、現在のインターフェイスのバージョンを取得することが必要になる場合があります。これを行うには、現在のオブジェクトのインターフェイス バージョンを取得します (例: this->getInterfaceVersion()またはmy_verの他のメソッド)。詳細については、「リモート オブジェクトのインターフェイス バージョンのクエリ」を参照してください。

新しい VINTF の安定したインターフェイス

新しい AIDL インターフェイス パッケージが追加されると、最後に凍結されたバージョンが存在しないため、 RELEASE_AIDL_USE_UNFROZENfalseの場合にフォールバックする動作はありません。これらのインターフェイスは使用しないでください。 RELEASE_AIDL_USE_UNFROZENfalseの場合、Service Manager はサービスによるインターフェイスの登録を許可せず、クライアントはインターフェイスを見つけられません。

デバイスのメイクファイルのRELEASE_AIDL_USE_UNFROZENフラグの値に基づいて、条件付きでサービスを追加できます。

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

サービスがより大きなプロセスの一部であるため、条件付きでデバイスに追加できない場合は、サービスがIServiceManager::isDeclared()で宣言されているかどうかを確認できます。宣言されていて登録に失敗した場合は、プロセスを中止します。宣言されていない場合は、登録に失敗することが予想されます。

開発ツールとしてのイカ

毎年、VINTF が凍結された後、来年のリリースで発売されるデバイスを反映するように、フレームワーク互換性マトリックス (FCM) のtarget-levelと Cuttlefish のPRODUCT_SHIPPING_API_LEVELを調整します。 target-levelPRODUCT_SHIPPING_API_LEVELを調整して、テストされ、来年のリリースの新しい要件を満たしている起動デバイスがあることを確認します。

RELEASE_AIDL_USE_UNFROZENtrueの場合、Cuttlefish は将来の Android リリースの開発に使用されます。これは、来年の Android リリースの FCM レベルとPRODUCT_SHIPPING_API_LEVELをターゲットにしており、次のリリースのベンダー ソフトウェア要件 (VSR) を満たす必要があります。

RELEASE_AIDL_USE_UNFROZENfalseの場合、Cuttlefish にはリリース デバイスを反映するための以前のtarget-levelPRODUCT_SHIPPING_API_LEVELがあります。 Android 14 以前では、この差別化は、FCM target-level 、出荷 API レベル、または次のリリースを対象とするその他のコードへの変更を反映しないさまざまな Git ブランチによって実現されます。

モジュールの命名規則

Android 11 では、バージョンと有効なバックエンドの組み合わせごとに、スタブ ライブラリ モジュールが自動的に作成されます。リンク用に特定のスタブ ライブラリ モジュールを参照するには、 aidl_interfaceモジュールの名前ではなく、スタブ ライブラリ モジュールの名前 ( ifacename - version - backendを使用します。

  • ifacename : aidl_interfaceモジュールの名前
  • version次のいずれかです
    • V version-number
    • V latest-frozen-version-number + 1ツリー先端 (まだ凍結されていない) バージョン)
  • backend次のいずれかです
    • Java バックエンドの場合はjava
    • C++ バックエンドの場合はcpp
    • NDK バックエンドの場合はndkまたはndk_platform 。前者はアプリ用、後者はプラットフォーム利用用で、
    • Rustバックエンド用のrust

fooという名前のモジュールがあり、その最新バージョンは2で、NDK と C++ の両方をサポートしているとします。この場合、AIDL は次のモジュールを生成します。

  • バージョン1に基づく
    • foo-V1-(java|cpp|ndk|ndk_platform|rust)
  • バージョン 2 (最新の安定バージョン) に基づいています
    • foo-V2-(java|cpp|ndk|ndk_platform|rust)
  • ToT バージョンに基づく
    • foo-V3-(java|cpp|ndk|ndk_platform|rust)

Android11と比べると、

  • 最新の安定バージョンを参照するfoo- backendは、 foo- V2 - backendになります。
  • ToT バージョンを参照していたfoo-unstable- backendfoo- V3 - backendになります

出力ファイル名は常にモジュール名と同じになります。

  • バージョン 1 に基づく: foo-V1-(cpp|ndk|ndk_platform|rust).so
  • バージョン 2 に基づく: foo-V2-(cpp|ndk|ndk_platform|rust).so
  • ToT バージョンに基づく: foo-V3-(cpp|ndk|ndk_platform|rust).so

AIDL コンパイラは、 unstableバージョンのモジュールも、安定した AIDL インターフェイス用のバージョン管理されていないモジュールも作成しないことに注意してください。 Android 12 以降、安定した AIDL インターフェイスから生成されるモジュール名には常にそのバージョンが含まれます。

新しいメタインターフェースメソッド

Android 10 では、安定した AIDL 用にいくつかのメタ インターフェイス メソッドが追加されています。

リモートオブジェクトのインターフェースバージョンの問い合わせ

クライアントは、リモート オブジェクトが実装しているインターフェイスのバージョンとハッシュをクエリし、返された値をクライアントが使用しているインターフェイスの値と比較できます。

cppバックエンドの例:

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 (およびndk_platform ) バックエンドの例:

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バックエンドの例:

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 言語の場合、リモート側は次のようにgetInterfaceVersion()getInterfaceHash()を実装しなければなりません (コピー/ペーストの間違いを避けるためにIFooの代わりにsuperが使用されます。警告を無効にするためにアノテーション@SuppressWarnings("static")が必要になる場合があります。 javac設定):

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

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

これは、生成されたクラス ( IFooIFoo.Stubなど) がクライアントとサーバー間で共有されるためです (たとえば、クラスはブート クラスパスに存在する可能性があります)。クラスが共有されている場合、サーバーは、古いバージョンのインターフェイスで構築されている場合でも、最新バージョンのクラスに対してリンクされます。このメタ インターフェイスが共有クラスに実装されている場合、常に最新バージョンが返されます。ただし、上記のようにメソッドを実装すると、インターフェイスのバージョン番号がサーバーのコードに埋め込まれるため ( IFoo.VERSION参照時にインライン化されるstatic final intであるため)、メソッドはサーバーが構築された正確なバージョンを返すことができます。と。

古いインターフェースの扱い

クライアントが新しいバージョンの AIDL インターフェイスで更新されても、サーバーが古い AIDL インターフェイスを使用している可能性があります。このような場合、古いインターフェースでメソッドを呼び出すとUNKNOWN_TRANSACTIONが返されます。

安定した AIDL により、クライアントはより詳細な制御を行うことができます。クライアント側では、デフォルトの実装を AIDL インターフェイスに設定できます。デフォルト実装のメソッドは、そのメソッドがリモート側に実装されていない場合にのみ呼び出されます (古いバージョンのインターフェイスで構築されているため)。デフォルトはグローバルに設定されるため、共有される可能性のあるコンテキストからは使用しないでください。

Android 13 以降の C++ の例:

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 での例:

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


foo.anAddedMethod(...);

AIDL インターフェイス内のすべてのメソッドのデフォルト実装を提供する必要はありません。リモート側で実装されることが保証されているメソッド (AIDL インターフェイスの記述にメソッドが含まれているときにリモートが構築されることが確実であるため) は、デフォルトのimplクラスでオーバーライドする必要はありません。

既存の AIDL を構造化/安定した AIDL に変換する

既存の AIDL インターフェイスとそれを使用するコードがある場合は、次の手順を使用してインターフェイスを安定した AIDL インターフェイスに変換します。

  1. インターフェースのすべての依存関係を特定します。インターフェイスが依存するパッケージごとに、そのパッケージが安定した AIDL で定義されているかどうかを確認します。定義されていない場合は、パッケージを変換する必要があります。

  2. インターフェイス内のすべての Parcelable を安定した Parcelable に変換します (インターフェイス ファイル自体は変更されないままにすることができます)。これを行うには、その構造を AIDL ファイルで直接表現します。これらの新しい型を使用するには、管理クラスを書き直す必要があります。これは、 aidl_interfaceパッケージ (下記) を作成する前に行うことができます。

  3. モジュールの名前、その依存関係、および必要なその他の情報を含むaidl_interfaceパッケージを作成します (前述のとおり)。 (構造化するだけでなく) 安定させるには、バージョン管理も必要です。詳細については、 「バージョニング インターフェイス」を参照してください。