WindowManager 拡張機能

アプリ デベロッパーは Jetpack WindowManager ライブラリを使用して、新しいデバイスのフォーム ファクタとマルチウィンドウ環境をサポートできます。

WindowManager 拡張機能(以下「拡張機能」)は、さまざまな Jetpack WindowManager 機能を有効にする、オプトイン方式の Android プラットフォーム モジュールです。このモジュールは、frameworks/base/libs/WindowManager/Jetpack の AOSP に実装され、WindowManager 機能をサポートしているデバイスに搭載されます。

拡張機能モジュールの配布

拡張機能がデバイスの makefile で有効になっている場合、.jar ライブラリにコンパイルされ、デバイス上の system_ext パーティションに配置されます。

デバイス上で拡張機能を有効にするには、製品デバイスの makefile に以下のように追加します。

$(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk)

これにより、デバイス上で androidx.window.extensions および androidx.window.sidecar パッケージが有効になり、persist.wm.extensions.enabled プロパティが設定されます。これらのパッケージを makefile に含めると、宣言も etc/permissions/ 内に配置され、アプリプロセスで使用できるようになります。通常これらのモジュールは、Jetpack WindowManager ライブラリで使用される場合、実行時にアプリプロセスの一環として読み込まれて実行されます。そのため、その動作は、次の図に示すようにクライアントサイドのフレームワーク コードに類似したものになります。

図 1. プラットフォーム コードに類似した形式でアプリプロセスに読み込まれる WindowManager 拡張機能

androidx.window.extensions モジュールは、現在開発中の最新の拡張機能モジュールです。androidx.window.sidecar モジュールは、Jetpack WindowManager の初期バージョンとの互換性を確保するために組み込まれていますが、サイドカーは現在積極的にはメンテナンスされていません。

次の図は、androidx.window.extensionsandroidx.window.sidecar のどちらを使用するかを決定するためのロジックを示しています。

図 2. androidx.window.extensionsandroidx.window.sidecar のどちらを利用するかを決めるためのフローチャート

拡張機能モジュール

拡張機能には、折りたたみ式の大画面デバイスと、外部ディスプレイでのウィンドウ処理をサポートするデバイス向けのウィンドウ処理機能が用意されています。次のような機能領域が含まれています。

OEM が拡張機能を実装する場合、デバイスのハードウェアが対応する機能をサポートしておらず、機能が互換性定義ドキュメント(CDD)7.1.1.1 で特に要求されていない限り、null コンポーネント、または WindowExtensions インターフェースのメソッドに対するデフォルトまたはスタブが実装されたコンポーネントを提供できます。

拡張機能と Jetpack API

WindowManager 拡張機能モジュールは、一般公開プラットフォーム API に加えて独自の API サーフェスを提供します。拡張機能モジュールは、非デベロッパー向けの androidx.window.extensions Jetpack ライブラリで公開されているため、Jetpack WindowManager(androidx.window)がコンパイル時にリンクできます。拡張機能 API サーフェスは、一般に下位レベルの API を提供します。

拡張機能によって提供される API は、Jetpack WindowManager ライブラリのみで使用するためのものです。拡張機能 API は、アプリ デベロッパーが直接呼び出すことを意図していません。機能が正しく動作するよう、拡張機能ライブラリを、アプリの依存関係として Gradle ビルドファイルに追加しないでください。また、アプリには直接プリコンパイルせず、実行時読み込みを使用して、実行時に拡張機能クラスが読み込まれるようにしてください。

Jetpack WindowManager(androidx.window)は、アプリの依存関係として追加し、WindowManager 拡張機能用 API などのデベロッパー向け公開 API を提供するためのものです。WindowManager ライブラリは、拡張機能をアプリプロセスに自動的に読み込んで、下位レベルの拡張機能 API を上位レベルの抽象化インターフェースおよびより目的を絞ったインターフェースにラップします。WindowManager Jetpack API は、最新の Android アプリ開発の標準に従っています。また、他の AndroidX ライブラリを使用するコードベースと緊密に連携することで、高い相互運用性を実現することを目的としています。

拡張機能のバージョンとアップデート

拡張機能モジュールは、Android プラットフォームの 1 年または四半期ごとのアップデートと合わせてアップデートされます。四半期ごとのアップデートにより、Android プラットフォームの API アップデートの間に拡張機能の API レベルが上がるため、迅速なイテレーションが可能になります。また、OEM は、ハードウェアの発売が近い新機能に対して公式 API を利用する機会が得られます。

次の表に、さまざまな Android リリースの androidx.window.extensions API バージョンを示します。

Android プラットフォーム バージョン WindowManager 拡張機能 API レベル androidx.window.extensions API バージョン
Android 15 6 1.5.0(近日提供予定)
Android 14 QPR3 5 1.4.0(近日提供予定)
Android 14 QPR1 4 1.3.0
Android 14 3 1.2.0
Android 13 QPR3 2 1.1.0
Android 13 1 1.0.0
Android 12L 1 1.0.0

拡張機能 API レベル(中央の列)は、既存の安定した API サーフェス(右側の列)が追加されるたびに上がります。

下位互換性および上位互換性

Jetpack WindowManager を利用すれば、頻繁な API レベルのアップデート、API の急速な変化、下位互換性の確保に伴う複雑な処理に対応できます。ライブラリ コードがアプリプロセスで実行されると、宣言された拡張機能 API レベルがライブラリによってチェックされ、宣言されたレベルに従って機能へのアクセスが提供されます。

アプリが実行時にクラッシュしないように、WindowManager では、宣言された拡張機能 API レベルに従って、使用可能な拡張機能 API の Java リフレクション チェックも実行時に行われます。不一致が検出されると、WindowManager は拡張機能の使用を無効にして(部分的または全体)、アプリでは該当する機能を使用できないことを報告できます。

WindowManager 拡張機能は、非公開のプラットフォーム API を使用して、WindowManager コア、DeviceStateManager、およびその他のシステム サービスを呼び出す system_ext モジュールとして実装されます。

対応する四半期または 1 年ごとの Android プラットフォームがリリースされる前(バージョンの最終決定前)の、プレリリース バージョンの拡張機能とは互換性が確保されていない場合があります。拡張機能 API の全履歴は、リリース ブランチ window:extensions:extensions API テキスト ファイルで確認できます。

新しいバージョンの拡張機能は、上位互換性を確保するためにアプリにコンパイルされる古いバージョンの WindowManager とともに動作し続ける必要があります。そのため、新しいバージョンの拡張機能 API では新しい API が追加されるだけで、古いバージョンは削除されません。したがって、古い WindowManager バージョンを使用するアプリでは、コンパイルされた古い拡張機能 API を引き続き使用できます。

CTS 検証では、デバイス上で宣言された拡張 API のバージョンについて、そのバージョンと以前のバージョンに対応するすべての API が存在し、機能することが確認されます。

パフォーマンス

Android 14(API レベル 34)から、拡張機能モジュールはデフォルトでは bootclasspath 以外のシステム クラスローダでキャッシュされます。そのため、アプリ起動時にモジュールをメモリに読み込むことによるパフォーマンス上の影響はありません。追加の IPC 呼び出しがクライアントとサーバー間で実行されると、個々のモジュール機能の使用はアプリのパフォーマンス特性に若干の影響を及ぼす可能性があります。

モジュール

アクティビティの埋め込み

アクティビティの埋め込みコンポーネントは、アプリが親アプリの境界内でアクティビティ ウィンドウの表示を整理するための一連の機能を備えています。これには、同時に 2 つのアクティビティをマルチペイン レイアウトで横に並べて表示し、従来のアプリを大画面に最適化するための機能も含まれています。

アクティビティの埋め込みコンポーネントは、sw600 dp 以上の大きさのディスプレイが搭載されているすべてのデバイスで使用できるようにする必要があります。アクティビティの埋め込みは、外部ディスプレイ接続をサポートするデバイスでも有効にする必要があります。実行時に外部ディスプレイが接続されていると、アプリが大きいサイズで表示される可能性があるためです。

デバイス設定

拡張機能モジュールの配布セクションで説明しているとおりに拡張機能モジュールを有効にすること以外は、特別なデバイス設定は必要ありません。マルチウィンドウ モードをサポートするすべてのデバイスで、拡張機能を有効にすることをおすすめします。将来の Android バージョンでは、一般的なハンドヘルド デバイスおよび大画面デバイスの設定で拡張機能が必須になる可能性があります。

ウィンドウ レイアウト情報

ウィンドウ レイアウト情報コンポーネントは、折りたたみ式デバイスのヒンジがアプリ ウィンドウにかかるときに、ヒンジの位置と状態を特定します。ウィンドウ レイアウト情報を利用することで、アプリは、折りたたみ式デバイスのテーブルトップ モードで最適なレイアウトを表示できます。使用方法の詳細については、アプリを折りたたみ対応にするをご覧ください。

折りたたみ式 Android デバイスにヒンジが含まれていて、個別または連続したディスプレイ パネル領域をつなげる場合は、WindowLayoutComponent を通じてアプリがヒンジに関する情報を利用できるようにする必要があります。

ヒンジの位置と境界は、API に渡される Context によって特定されるアプリ ウィンドウを基準にして報告する必要があります。アプリ ウィンドウの境界がヒンジの境界と交差していない場合は、ヒンジの DisplayFeature は報告しません。ユーザーがマルチ ウィンドウ モードまたは互換レターボックス表示モードでアプリ ウィンドウを自由に移動できる場合など、位置を確実に報告できない可能性がある場合は、ディスプレイ特性を報告しないようにすることも可能です。

折りたたみ機能の場合は、安定した状態間でヒンジの位置が変更されたときに状態の更新を報告する必要があります。フラット ディスプレイ状態のデフォルトでは、API で FoldingFeature.State.FLAT を報告する必要があります。デバイスのハードウェアを安定した状態で 2 つ折りモードのままにできる場合は、API で FoldingFeature.State.HALF_OPENED を報告する必要があります。API には closed 状態はありませんが、発生している場合は、アプリ ウィンドウが表示されていないか、ヒンジの境界を越えていないことが考えられます。

デバイス設定

折りたたみ機能の実装をサポートするために、OEM は次のことを行う必要があります。

  • DeviceStateManagerService で使用するデバイスの状態を device_state_configuration.xml に設定します。参考までに、DeviceStateProviderImpl.java をご覧ください。

    DeviceStateProvider または DeviceStatePolicy のデフォルトの実装がデバイスに適していない場合は、カスタム実装を使用できます。

  • 拡張機能モジュールの配布セクションの説明に沿って拡張機能モジュールを有効にします。

  • com.android.internal.R.string.config_display_features 文字列リソースでディスプレイ特性の場所を指定します(通常は、デバイス オーバーレイの frameworks/base/core/res/res/values/config.xml 内)。

    想定される文字列の形式は次のとおりです。

    <type>-[<left>,<top>,<right>,<bottom>]

    typefold または hinge です。lefttoprightbottom の値は、自然なディスプレイの向きにおけるディスプレイ座標空間の整数のピクセル座標です。設定文字列には、セミコロンで区切って複数のディスプレイ特性を指定できます。

    次に例を示します。

    <!-- Jetpack WindowManager display features -->
    <string name="config_display_features" translatable="false">fold-[1000,0,1000,2000]</string>
    
  • DeviceStateManager で使用される内部デバイス状態識別子と com.android.internal.R.array.config_device_state_postures でデベロッパーに送信されるパブリック状態定数間のマッピングを定義します。

    各エントリで想定している形式は次のとおりです。

    <device_specific_state_identifier>:<Jetpack WindowManager state identifier>

    サポートされている状態識別子は次のとおりです。

    • COMMON_STATE_NO_FOLDING_FEATURES = 1: この状態では、報告する折りたたみ特性はありません。たとえば、メイン画面が内側にある一般的な内折り式デバイスが閉じた状態になっている可能性があります。
    • COMMON_STATE_HALF_OPENED = 2: デバイスが半分開いた状態です。
    • COMMON_STATE_FLAT = 3: デバイスが平坦に開いた状態です。たとえば、メイン画面が内側にある一般的な内折り式デバイスが開いた状態になっている可能性があります。
    • COMMON_STATE_USE_BASE_STATE = 1000: Android 14 では、エミュレートされた状態に使用できる値です。CommonFoldingFeature.java で定義されているとおり、ヒンジ状態がベース状態に基づいて導出されます。

    詳しくは、DeviceStateManager.DeviceStateCallback#onBaseStateChanged(int) をご覧ください。

    次に例を示します。

    <!-- Map of System DeviceState supplied by DeviceStateManager to WindowManager posture.-->
    <string-array name="config_device_state_postures" translatable="false">
        <item>0:1</item>    <!-- CLOSED       : COMMON_STATE_NO_FOLDING_FEATURES -->
        <item>1:2</item>    <!-- HALF_OPENED  : COMMON_STATE_HALF_OPENED -->
        <item>2:3</item>    <!-- OPENED       : COMMON_STATE_FLAT -->
        <item>3:1</item>    <!-- REAR_DISPLAY : COMMON_STATE_NO_FOLDING_FEATURES -->
        <item>4:1000</item> <!-- CONCURRENT   : COMMON_STATE_USE_BASE_STATE -->
    </string-array>
    

ウィンドウ領域

ウィンドウ領域コンポーネントには、一部の折りたたみ式デバイスやマルチディスプレイ デバイスの追加のディスプレイやディスプレイ領域に、アプリからアクセスできるようにするための一連の機能が用意されています。

背面ディスプレイ モードでは、アプリで折りたたみ式デバイスのカバー ディスプレイ上にカメラ プレビュー UI が表示され、自撮りや動画撮影用にメインのデバイスカメラを使用できるようになります。背面デバイスカメラと連携する Android 対応(サイズ、密度、使用可能なナビゲーション アフォーダンスなどの属性に関する Android CDD の定義に準拠している)カバー ディスプレイを搭載したデバイスは、背面ディスプレイ モードにアクセスできるようにする必要があります。

Android 14 では、デュアル ディスプレイ モードによって、折りたたみ式デバイスのインナー ディスプレイでアプリを実行し、他のユーザーが見ているカバー ディスプレイに追加のコンテンツを表示できます。たとえば、カバー ディスプレイを使って、カメラのプレビューを撮影または録画する相手に見せられます。

デバイス設定

折りたたみ機能の実装をサポートするために、OEM は次のことを行う必要があります。

  • DeviceStateManagerService で使用するデバイスの状態を device_state_configuration.xml に設定します。詳しくは、DeviceStateProviderImpl.java をご覧ください。

    DeviceStateProvider または DeviceStatePolicy のデフォルトの実装がデバイスに適していない場合は、カスタム実装を使用できます。

  • オープンモードまたはフラットモードをサポートする折りたたみ式デバイスの場合は、対応する状態識別子を com.android.internal.R.array.config_openDeviceStates に指定します。

  • 折りたたんだ状態をサポートする内折り式デバイスの場合は、対応する状態識別子を com.android.internal.R.array.config_foldedDeviceStates にリストします。

  • 半分折りたたんだ状態をサポートする内折り式デバイス(ヒンジがノートパソコンのように半分開いた状態になる)の場合は、対応する状態識別子を com.android.internal.R.array.config_halfFoldedDeviceStates にリストします。

  • 背面ディスプレイ モードをサポートするデバイスの場合:

    • DeviceStateManagercom.android.internal.R.array.config_rearDisplayDeviceStates に対応する状態をリストします。
    • com.android.internal.R.string.config_rearDisplayPhysicalAddress に背面ディスプレイの物理ディスプレイ アドレスを指定します。
    • 拡張機能で使用する状態識別子を com.android.internal.R.integer.config_deviceStateRearDisplay に指定します。
    • com.android.internal.R.array.config_deviceStatesAvailableForAppRequests に状態識別子を追加して、アプリで使用できるようにします。
  • Android 14 でデュアル(同時)ディスプレイ モードをサポートするデバイスの場合:

    • com.android.internal.R.bool.config_supportsConcurrentInternalDisplaystrue に設定します。
    • com.android.internal.R.config_deviceStateConcurrentRearDisplay に背面ディスプレイの物理ディスプレイ アドレスを指定します。
    • 識別子をアプリで使用できるようにする場合は、拡張機能で使用する状態識別子を com.android.internal.R.integer.config_deviceStateConcurrentRearDisplay に指定します。
    • com.android.internal.R.array.config_deviceStatesAvailableForAppRequests に状態識別子を追加して、アプリで使用できるようにします。

検証

OEM は、一般的なシナリオで想定される動作を保証するために実装を検証する必要があります。実装のテストには、CTS テストおよび Jetpack WindowManager によるテストを利用できます。

CTS テスト

CTS テストを実行するには、CTS テストを実行するをご覧ください。Jetpack WindowManager に関連する CTS テストは cts/tests/framework/base/windowmanager/jetpack/ にあります。テスト モジュール名は CtsWindowManagerJetpackTestCases です。

WindowManager テスト

Jetpack WindowManager テストをダウンロードするには、Android Jetpack の手順をご覧ください。テストは window:window モジュールの下のウィンドウ ライブラリにあります(window/window/src/androidTest/)。

コマンドラインから window:window モジュールのデバイステストを実行するには、次の手順を実施します。

  1. 開発者向けオプションが用意され、USB デバッグが有効になっているデバイスをプラグインする
  2. パソコンでデバイスをデバッグできるようにする
  3. androidx リポジトリのルート ディレクトリでシェルを開く
  4. framework/support ディレクトリに移動する
  5. ./gradlew window:window:connectedAndroidTest コマンドを実行する
  6. 結果を分析する

Android Studio からテストを実行するには、次の手順を実施します。

  1. Android Studio を開く
  2. 開発者向けオプションが用意され、USB デバッグが有効になっているデバイスをプラグインする
  3. パソコンでデバイスをデバッグできるようにする
  4. ウィンドウ モジュールのウィンドウ ライブラリ内のテストに移動する
  5. テストクラスを開き、エディタの右側にある緑の矢印を使用して実行する

Android Studio で設定を作成し、テストメソッド、テストクラス、またはモジュール内のすべてのテストを実行することもできます。

結果は、シェルの出力を確認して手動で分析できます。デバイスが特定の前提条件を満たしていない場合、一部のテストはスキップされます。結果は標準の場所に保存されます。アナリストは、スクリプトを作成して結果を自動で分析することも可能です。