Android 10 では、安定版の Android インターフェース定義言語(AIDL)のサポートが追加されています。これは、AIDL インターフェースによって提供されるアプリケーション プログラミング インターフェース(API)とアプリケーション バイナリ インターフェース(ABI)をトラッキングするための新しい方法です。安定版 AIDL は AIDL とまったく同じように動作しますが、ビルドシステムがインターフェースの互換性のトラッキングを行い、開発者ができることに関しては制約があります。
- インターフェースは、ビルドシステムで
aidl_interfaces
を使用して定義されています。 - インターフェースには、構造化データのみを含めることができます。目的のタイプの Parcelable は、AIDL 定義に基づいて自動的に作成され、マーシャリングとマーシャリング解除が自動的に行われます。
- インターフェースを stable(下位互換性あり)として宣言できます。その場合、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 インターフェースのいずれかが、別のaidl_interface
のインターフェース、または Parcelable を使用している場合は、その名前をここに入力します。名前自体を指定して最新バージョンを参照することも、バージョン サフィックスを付けた名前(-V1
など)を指定して特定のバージョンを参照することもできます。バージョンの指定は、Android 12 以降でサポートされています。versions
:api_dir
の下でフリーズされている以前のバージョンのインターフェース。Android 11 以降では、versions
はaidl_api/name
の下でフリーズされます。フリーズされたバージョンのインターフェースが存在しない場合、versions は指定しないでください。また、互換性チェックは行われません。このフィールドは、Android 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"
のみをサポートしています。stability
が設定されていない場合、unstable
が指定されていない限り、このインターフェースが後方互換であることをビルドシステムが確認します。設定されていない場合、このコンパイル コンテキスト内の安定したインターフェースに対応します(つまり、system.img
と関連するパーティションにあるものなどのシステム関連のすべてのものか、vendor.img
と関連するパーティションにあるものなどのベンダー関連のすべてのもののいずれか)。stability
が"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 つのバックエンドのいずれかが不要な場合は、明示的に無効にする必要があります。Android 15 までは 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 の主な違いは、Parcelable の定義方法です。以前は、Parcelable は前方宣言されていました。安定版(したがって構造化された)AIDL では、Parcelable のフィールドと変数は明示的に定義されています。
// 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 your preference 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: ...,
rustlibs: ["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 の管理に使用できるターゲットもビルドシステムに作成されます。ビルド時に、Android のバージョンに応じて、新しい API 定義が foo-freeze-api によって api_dir
または aidl_api/name
に追加され、.hash
ファイルが追加されます。どちらも、インターフェースの新たにフリーズされたバージョンを表します。foo-freeze-api によって、追加のバージョンとそのバージョンの imports
を反映するように versions_with_info
プロパティも更新されます。基本的に、versions_with_info
の imports
は imports
フィールドからコピーされます。ただし、インポートについては最新の安定版が versions_with_info
の imports
に指定されていますが、明示的なバージョンはありません。versions_with_info
プロパティが指定されると、フリーズされたバージョン間と、Top of Tree(ToT)と最新のフリーズされたバージョン間で、ビルドシステムによる互換性チェックが行われます。
また、ToT バージョンの API 定義を管理することも必要です。API が更新されるたびに、foo-update-api を実行し、ToT バージョンの API 定義が含まれている aidl_api/name/current
を更新します。
インターフェースの安定性を維持するために、オーナーは以下を行うことができます。
- 新しいメソッド(または新しいシリアル番号が明示的に定義されたメソッド)をインターフェースの最後に追加する
- 新しい要素を Parcelable の最後に追加する(要素ごとにデフォルトの追加が必要)
- 新しい定数値を追加する
- 新しい列挙子を追加する(Android 11 の場合)
- 新しいフィールドを union の最後に追加する(Android 12 の場合)
その他のアクションは許可されません。また、オーナー以外がインターフェースを変更することもできません(そうしないと、オーナーが行った変更との間で競合が生じるおそれがあります)。
すべてのインターフェースがリリースのためにフリーズされているかをテストするには、次の環境変数を設定してビルドします。
AIDL_FROZEN_REL=true m ...
- ビルドで、owner:
フィールドが指定されていないすべての安定版 AIDL インターフェースをフリーズする必要があります。AIDL_FROZEN_OWNERS="aosp test"
- ビルドで、owner:
フィールドが「aosp」または「test」として指定されたすべての安定版 AIDL インターフェースをフリーズする必要があります。
インポートの安定性
インターフェースのフリーズ バージョンに対するインポートのバージョンの更新は、安定版 AIDL レイヤで下位互換性があります。ただし、これらを更新すると、以前のバージョンのインターフェースを使用しているすべてのサーバーとクライアントを更新する必要があり、異なるバージョンのタイプが混在すると一部のアプリが混乱する可能性があります。一般に、タイプのみのパッケージまたは共通パッケージでは、IPC トランザクションの不明なタイプを処理するコードがすでに作成されている必要があるため、これは安全です。
Android プラットフォームのコードでは、android.hardware.graphics.common
が、このタイプのバージョン アップグレードの最たる例です。
バージョニングされたインターフェースを使用する
インターフェース メソッド
実行時に、古いサーバーで新しいメソッドを呼び出そうとすると、バックエンドに応じて、新しいクライアントでエラーか例外が発生します。
cpp
バックエンドが::android::UNKNOWN_TRANSACTION
を取得します。ndk
バックエンドがSTATUS_UNKNOWN_TRANSACTION
を取得します。java
バックエンドがandroid.os.RemoteException
を取得し、API が実装されていないことを示すメッセージが表示されます。
これを処理するための戦略については、バージョンをクエリするとデフォルトを使用するをご覧ください。
Parcelable
新しいフィールドが Parcelable に追加されると、古いクライアントやサーバーはそれらを削除します。新しいクライアントやサーバーが古い Parcelable を受信すると、新しいフィールドのデフォルト値が自動的に入力されます。つまり、Parcelable 内のすべての新しいフィールドには、デフォルトを指定する必要があります。
フィールドが定義されているバージョンを実装していることを認識している場合を除き、クライアントは、サーバーが新しいフィールドを使用することを想定すべきではありません(バージョンをクエリするを参照)。
列挙型と定数
同様に、クライアントやサーバーは必要に応じて、未確認の定数値や列挙子を拒否または無視する必要があります。今後さらに追加される可能性があるためです。たとえば、サーバーは認識していない列挙子を受け取った場合に中止すべきではありません。サーバーは列挙子を無視するか、何かを返して、この実装でサポートされていないことをクライアントが認識できるようにする必要があります。
共用体
新しいフィールドで union を送信しようとする際、レシーバーが古くそのフィールドを認識できない場合は失敗します。実装では、新しいフィールドの union が認識されません。一方向のトランザクションの場合、このエラーは無視されます。それ以外の場合、エラーは BAD_VALUE
(C++ または NDK バックエンドの場合)または IllegalArgumentException
(Java バックエンドの場合)になります。このエラーは、クライアントが新しいフィールドに設定された union を古いサーバーに送信するか、古いクライアントが新しいサーバーから union を受信している場合に受信されます。
複数のバージョンを管理する
Android のリンカー名前空間では、特定の aidl
インターフェースには 1 つのバージョンのみが許されます。これは、生成される aidl
タイプが複数の定義を持たないようにするためです。C++ には、各シンボルに 1 つの定義のみという単一定義ルールがあります。
Android ビルドでは、1 つのモジュールが同じ aidl_interface
ライブラリの異なるバージョンに依存していると、エラーとなります。直接依存している場合もあれば、依存関係の依存関係を通じて間接的に依存している場合もあります。これらのエラーには、aidl_interface
ライブラリの競合しているバージョンの依存関係グラフが表示されます。すべての依存関係は、これらのライブラリの同じ(通常は最新の)バージョンを含むように修正する必要があります。
インターフェース ライブラリが多数のモジュールから使用されている場合、同じバージョンを使用する必要のあるライブラリとプロセスのグループに cc_defaults
、java_defaults
、rust_defaults
を作成することも解決に役立ちます。インターフェースの新しいバージョンを導入する際に、さきほどのデフォルトを更新すると、それらを使用するモジュールのすべても合わせて更新され、インターフェースの異なるバージョンが使用されることはなくなります。
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
モジュールで他の aidl_interface
モジュールをインポートすると、特定のバージョンが共に使用されることを要求する新たな依存関係が生まれます。この状況で、同じプロセスでいっしょに使用されている複数の aidl_interface
モジュールでインポートされている共通の aidl_interface
モジュールが存在すると、管理が困難になります。
aidl_interfaces_defaults
は、1 か所で更新できる aidl_interface
の依存関係の最新バージョンの定義を 1 つだけにするために使用でき、その共通のインターフェースをインポートしようとするすべての aidl_interface
から使用できます。
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"],
...
}
フラグベースの開発
開発中(フリーズされていない)のインターフェースは、下位互換性が保証されていないため、リリース デバイスでは使用できません。
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 を削除できます。
マニフェスト
Android 15 では、RELEASE_AIDL_USE_UNFROZEN
の値に基づいてビルド時にマニフェスト ファイルを変更するための libvintf
に修正が行われました。
マニフェストとマニフェスト フラグメントでは、サービスがどのバージョンのインターフェースを実装するかを宣言します。インターフェースのフリーズされていない最新のバージョンを使用する場合、この新しいバージョンを反映するようにマニフェストを更新する必要があります。RELEASE_AIDL_USE_UNFROZEN=false
の場合、マニフェストのエントリは、生成された AIDL ライブラリの変更を反映するように libvintf
によって調整されます。フリーズされていないバージョン N
から最後のフリーズされたバージョン N - 1
に修正されます。そのため、各サービスについて、複数のマニフェストやマニフェスト フラグメントを管理する必要はありません。
HAL クライアントの変更
HAL クライアント コードは、これまでサポートされてきたフリーズ バージョンそれぞれと下位互換性を保つ必要があります。RELEASE_AIDL_USE_UNFROZEN
が false
の場合、サービスは常に最新のフリーズ バージョンまたはそれ以前のフリーズ バージョンと同じように動作します(たとえば、フリーズされていない新しいメソッドを呼び出すと UNKNOWN_TRANSACTION
が返されたり、新しい parcelable
フィールドがデフォルト値を持っていたりします)。Android フレームワーク クライアントでは、追加された以前のバージョンとの下位互換性が要求されますが、ベンダー クライアントやパートナー所有インターフェースのクライアントにとっては新たなものです。
HAL 実装の変更
HAL 開発とフラグベース開発の最大の違いは、HAL 実装では、RELEASE_AIDL_USE_UNFROZEN
が false
の場合に動作するように、最新のフリーズ バージョンとの下位互換性が要求されることです。実装とデバイスコードで下位互換性を考慮することは新しい作業となります。バージョニングされたインターフェースを使用するをご覧ください。
下位互換性を考慮することは、クライアントとサーバー間および、フレームワーク コードとベンダーコード間では基本的に同じですが、同じソースコード(フリーズされていない現在のバージョン)を使用する 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 はサービスにインターフェースの登録を許可せず、クライアントはそのインターフェースを見つけられません。
デバイスの makefile の RELEASE_AIDL_USE_UNFROZEN
フラグの値に基づいて、条件付きでサービスを追加できます。
ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
android.hardware.health.storage-service
endif
サービスがより大きなプロセスの一部であり、デバイスに条件付きで追加できない場合は、IServiceManager::isDeclared()
でサービスが宣言されているかどうかを確認できます。宣言されていて登録に失敗した場合は、プロセスを中断します。宣言されていない場合は、登録に失敗すると想定されます。
開発ツールとしての Cuttlefish
毎年 VINTF がフリーズされた後、Cuttlefish のフレームワーク互換性マトリックス(FCM)の target-level
と 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 以前のバージョンでは、この区別は、Git ブランチを分け、FCM target-level
、出荷時の API レベル、次のリリースを対象とするその他のコードへの変更を反映しないことで行います。
モジュールの命名規則
Android 11 では、バージョンと有効にしたバックエンドの組み合わせごとに、スタブ ライブラリ モジュールが自動的に作成されます。リンクのために特定のスタブ ライブラリ モジュールを参照するには、aidl_interface
モジュールの名前ではなく、スタブ ライブラリ モジュールの名前(ifacename-version-backend)を使用します。
ifacename
:aidl_interface
モジュールの名前version
は次のいずれかです。- フリーズされたバージョンの場合は
Vversion-number
- 最新の(まだフリーズされていない)バージョンの場合は
Vlatest-frozen-version-number + 1
- フリーズされたバージョンの場合は
backend
は次のいずれかです。- Java バックエンドの場合は
java
、 - C++ バックエンドの場合は
cpp
、 - NDK バックエンドの場合は
ndk
またはndk_platform
です。Android 13 までは、前者はアプリ向け、後者はプラットフォーム向けです。Android 13 移行は、ndk
のみを使用してください。 - 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)
Android 11 と比較した場合:
- 最新の安定版を参照する
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
を使用します。javac
構成によっては、警告を無効にするためにアノテーション @SuppressWarnings("static")
が必要になることがあります)。
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 インターフェースで指定する必要はありません。リモート側で実装されることが保証されているメソッドは、デフォルトの impl
クラスでオーバーライドする必要はありません(それらのメソッドが AIDL インターフェースの記述内にあれば、リモート側で確実にビルドされるため)。
既存の AIDL を構造化された AIDL または安定版 AIDL に変換する
既存の AIDL インターフェースとそれを使用するコードがある場合、インターフェースを安定版 AIDL インターフェースに変換するには、次の手順を実施します。
インターフェースのすべての依存関係を特定します。インターフェースが依存しているすべてのパッケージについて、パッケージが安定版 AIDL で定義されているかどうかを確認します。定義されていない場合は、パッケージを変換する必要があります。
インターフェース内のすべての Parcelable を stable Parcelable に変換します(インターフェース ファイル自体は変更されません)。この変換は、AIDL ファイルで構造を直接表現することによって行います。これらの新しいタイプを使用するには、管理クラスを書き直す必要があります。これは、
aidl_interface
パッケージの作成前に行うことができます(下記参照)。モジュールの名前、依存関係、その他の必要な情報を含む
aidl_interface
パッケージを作成します(前述の説明を参照)。構造化だけでなく安定化も実現するには、バージョニングを行う必要もあります。 詳細については、インターフェースのバージョニングをご覧ください。