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;
}
現在、 boolean
、 char
、 float
、 double
、 byte
、 int
、 long
、および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_info
のimports
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_UNFROZEN
がfalse
の場合にフリーズされたバージョン 4 が使用されるため、互換性マトリックスからバージョン 3 を削除できます。
HAL クライアントの変更
HAL クライアント コードは、以前にサポートされていた各凍結バージョンと下位互換性がある必要があります。 RELEASE_AIDL_USE_UNFROZEN
がfalse
の場合、サービスは常に最後に凍結されたバージョン以前と同じように見えます (たとえば、新しい解凍されたメソッドを呼び出すとUNKNOWN_TRANSACTION
が返されるか、新しいparcelable
フィールドがデフォルト値になります)。 Android フレームワーク クライアントは、追加の前バージョンとの下位互換性を維持する必要がありますが、これはベンダー クライアントおよびパートナー所有のインターフェイスのクライアントにとっては新しい詳細です。
HAL 実装の変更
HAL 開発とフラグベースの開発の最大の違いは、 RELEASE_AIDL_USE_UNFROZEN
false
の場合に動作するために、HAL 実装が最後に凍結されたバージョンと下位互換性を持つ必要があることです。実装とデバイス コードにおける下位互換性を考慮することは、新たな課題です。 「バージョン管理されたインターフェイスの使用」を参照してください。
下位互換性に関する考慮事項は、クライアントとサーバー、およびフレームワーク コードとベンダー コードについては通常同じですが、同じソース コードを使用する 2 つのバージョンを効果的に実装することになるため、注意が必要な微妙な違いがあります。 (現在の、凍結されていないバージョン)。
例: インターフェイスには 3 つの凍結バージョンがあります。インターフェースが新しいメソッドで更新されました。クライアントとサービスは両方とも、新しいバージョン 4 ライブラリを使用するように更新されます。 V4 ライブラリはインターフェイスの凍結されていないバージョンに基づいているため、 RELEASE_AIDL_USE_UNFROZEN
がfalse
の場合、最後の凍結バージョンであるバージョン 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_UNFROZEN
がfalse
、サービスが送信しようとする新しいフィールドの値がプロセスの途中でドロップされる場合、既存の型 ( parcelable
、 enum
、 union
) の新しいフィールドが存在しないか、デフォルト値が含まれている可能性があります。
この非凍結バージョンに追加された新しいタイプは、インターフェイスを介して送受信できません。
RELEASE_AIDL_USE_UNFROZEN
がfalse
の場合、実装はクライアントから新しいメソッドの呼び出しを取得しません。
新しい列挙子は、以前のバージョンではなく、導入されたバージョンでのみ使用するように注意してください。
通常、リモート インターフェイスが使用しているバージョンを確認するには、 foo->getInterfaceVersion()
を使用します。ただし、フラグベースのバージョン管理サポートを使用すると、2 つの異なるバージョンを実装することになるため、現在のインターフェイスのバージョンを取得することが必要になる場合があります。これを行うには、現在のオブジェクトのインターフェイス バージョンを取得します (例: this->getInterfaceVersion()
またはmy_ver
の他のメソッド)。詳細については、「リモート オブジェクトのインターフェイス バージョンのクエリ」を参照してください。
新しい VINTF の安定したインターフェイス
新しい AIDL インターフェイス パッケージが追加されると、最後に凍結されたバージョンが存在しないため、 RELEASE_AIDL_USE_UNFROZEN
がfalse
の場合にフォールバックする動作はありません。これらのインターフェイスは使用しないでください。 RELEASE_AIDL_USE_UNFROZEN
がfalse
の場合、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-level
とPRODUCT_SHIPPING_API_LEVEL
を調整して、テストされ、来年のリリースの新しい要件を満たしている起動デバイスがあることを確認します。
RELEASE_AIDL_USE_UNFROZEN
がtrue
の場合、Cuttlefish は将来の Android リリースの開発に使用されます。これは、来年の Android リリースの FCM レベルとPRODUCT_SHIPPING_API_LEVEL
をターゲットにしており、次のリリースのベンダー ソフトウェア要件 (VSR) を満たす必要があります。
RELEASE_AIDL_USE_UNFROZEN
がfalse
の場合、Cuttlefish にはリリース デバイスを反映するための以前のtarget-level
とPRODUCT_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
。
- Java バックエンドの場合は
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- backend
はfoo- 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; }
}
これは、生成されたクラス ( IFoo
、 IFoo.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 インターフェイスに変換します。
インターフェースのすべての依存関係を特定します。インターフェイスが依存するパッケージごとに、そのパッケージが安定した AIDL で定義されているかどうかを確認します。定義されていない場合は、パッケージを変換する必要があります。
インターフェイス内のすべての Parcelable を安定した Parcelable に変換します (インターフェイス ファイル自体は変更されないままにすることができます)。これを行うには、その構造を AIDL ファイルで直接表現します。これらの新しい型を使用するには、管理クラスを書き直す必要があります。これは、
aidl_interface
パッケージ (下記) を作成する前に行うことができます。モジュールの名前、その依存関係、および必要なその他の情報を含む
aidl_interface
パッケージを作成します (前述のとおり)。 (構造化するだけでなく) 安定させるには、バージョン管理も必要です。詳細については、 「バージョニング インターフェイス」を参照してください。