Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

ディスプレイのサポート

ディスプレイ固有の領域に対する更新内容は次のとおりです。

アクティビティとディスプレイのサイズ変更

アプリがマルチウィンドウ モードまたはサイズ変更をサポートしていない可能性があることを示すために、アクティビティで resizeableActivity=false 属性が使用されます。アクティビティのサイズを変更したときにアプリで発生する一般的な問題は次のとおりです。

  • アプリや他の非視覚的コンポーネントと異なる設定がアクティビティに適用される場合がある。よくある間違いは、アプリのコンテキストからディスプレイの指標を読み取ることです。戻り値は、アクティビティが表示される可視領域の指標に合わせて調整されません。
  • アクティビティで、サイズ変更やクラッシュが処理されずに歪んだ UI が表示されたり、インスタンスの状態を保存せずに再起動が行われて状態が不明になったりする場合がある。
  • アプリが、ウィンドウ位置の相対座標ではなく、絶対座標入力を使用してマルチウィンドウでの入力を破棄する場合がある。

Android 7 以降では、アプリが常に全画面モードで実行されるように resizeableActivity=false を設定できます。この場合、サイズ変更できないアクティビティは分割画面に表示されません。分割画面モードで、サイズ変更できないアクティビティをランチャーから起動しようとすると、プラットフォームによって分割画面モードが終了され、サイズ変更できないアクティビティが全画面モードで起動されます。

マニフェストで明示的にこの属性が false に設定されているアプリは、マルチウィンドウ モードで起動しないでください。ただし、次のように互換モードが適用される場合を除きます。

  • すべてのアクティビティとアクティビティ以外のコンポーネントを含む同じ設定がプロセスに適用される。
  • 適用される設定がアプリ対応ディスプレイの CDD 要件を満たしている。

Android 10 のプラットフォームでも、サイズ変更できないアクティビティは分割画面モードになりませんが、固定の向きまたはアスペクト比が宣言されているアクティビティは一時的に調整できます。宣言されていないアクティビティは、Android 9 以前と同じように画面全体に表示されます。

デフォルトの実装では、次のポリシーが適用されます。

android:resizeableActivity 属性によって、マルチウィンドウと互換性がないことを宣言されたアクティビティが以下のいずれかの条件を満たしている場合、適用された画面設定の変更が必要なときはアクティビティとプロセスが元の設定で保存され、ユーザーはアプリプロセスを再開して更新された画面設定を使用できます。

  • android:screenOrientation を使用して向きが固定されている
  • API レベルをターゲットとすることで、デフォルトの最大アスペクト比または最小アスペクト比が設定されているか、アスペクト比が明示的に宣言されている

サイズ変更できないアクティビティは、宣言されたアスペクト比で表示されます。デバイスを折りたたむと、適切なレターボックスを使用してアスペクト比を維持しながら、表示領域に合わせてウィンドウが縮小されます。さらに、アクティビティの表示領域が変更されるたびに、アクティビティ再開項目が表示されます。

デバイスを開くと、アクティビティの設定、サイズ、アスペクト比は変わらずに、アクティビティを再開する項目が表示されます。

resizeableActivity が設定されていない(または true に設定されている)場合、アプリはサイズ変更を完全にサポートしています。

実装

固定の向きまたはアスペクト比が設定されたサイズ変更できないアクティビティは、コードではサイズ互換モード(SCM)と呼ばれます。条件は ActivityRecord#shouldUseSizeCompatMode() で定義されます。SCM アクティビティが起動されると、リクエストされたオーバーライド設定で画面関連の設定(サイズや密度など)が固定されるため、アクティビティは現在のディスプレイ設定に依存しなくなります。

全画面で表示できない SCM アクティビティは、上部の中央に配置されます。アクティビティの境界は、AppWindowToken#calculateCompatBoundsTransformation() によって計算されます。

SCM アクティビティでコンテナと異なる画面設定が使用され(ディスプレイのサイズが変更されたり、アクティビティが別のディスプレイに移動したりした場合など)、ActivityRecord#inSizeCompatMode() が true に設定されている場合、SizeCompatModeActivityController(システム UI)はコールバックを受信してプロセス再開ボタンを表示します。

ディスプレイ サイズとアスペクト比

Android 10 は、比率の高い細長い画面から 1 対 1 までの新しいアスペクト比に対応しています。アプリは、処理できる画面の ApplicationInfo#maxAspectRatioApplicationInfo#minAspectRatio を定義できます。

図 1. Android 10 でサポートされるアプリの比率の例

デバイスの実装では、Android 9 以前の要件よりもサイズと解像度が小さい(幅または高さが最小で 2.5 インチ、smallestScreenWidth が最小で 320 DP)セカンダリ ディスプレイを使用できますが、表示できるのは、これらの小さなディスプレイに対応するアクティビティのみです。

アプリで、対象のディスプレイ サイズ以下の最小サポートサイズを宣言して選択できます。この場合は、AndroidManifest で android:minHeightandroid:minWidth のアクティビティ レイアウト属性を使用します。

ディスプレイ ポリシー

Android 10 では、特定のディスプレイ ポリシーが PhoneWindowManager のデフォルトの WindowManagerPolicy 実装から分離され、次のようなディスプレイ単位のクラスに移動されています。

  • ディスプレイの状態と回転
  • 一部のキーとモーション イベントのトラッキング
  • システム UI とデコレーション ウィンドウ

Android 9 以前では、ディスプレイ ポリシー、状態と設定、回転、デコレーション ウィンドウ フレーム トラッキングなどが PhoneWindowManager クラスで処理されました。Android 10 では、DisplayRotation に移動された回転トラッキング以外は DisplayPolicy クラスに移動されています。

ディスプレイのウィンドウ設定

Android 10 では、ディスプレイごとに設定可能なウィンドウ設定が拡張され、次の項目が含まれるようになりました。

  • デフォルトのディスプレイ ウィンドウ モード
  • オーバースキャン値
  • ユーザー回転と回転モード
  • 適用サイズ、密度、スケーリング モード
  • コンテンツ移転モード(ディスプレイが取り外された場合)
  • システム デコレーションと IME のサポート

DisplayWindowSettings クラスには、これらの項目の設定が含まれています。これらの設定が変更されるたびに、ディスクの display_settings.xml/data パーティションに保存されます。詳しくは、DisplayWindowSettings.AtomicFileStorageDisplayWindowSettings#writeSettings() をご覧ください。デバイス メーカーは、デバイス設定のデフォルト値を display_settings.xml で提供できます。ただし、ファイルは /data に保存されるため、ワイプによって消去されたファイルを復元するには追加のロジックが必要になることがあります。

デフォルトで、Android 10 では設定を持続する際のディスプレイの ID として DisplayInfo#uniqueId が使用されます。すべてのディスプレイについて uniqueId を入力する必要があります。また、物理ディスプレイとネットワーク ディスプレイについても同様です。ID として物理ディスプレイのポートを使用することもできます。これは DisplayWindowSettings#mIdentifier で設定できます。ストレージのディスプレイ エントリに使用する鍵を安全に更新するために、書き込み時には常にすべての設定が対象になります。詳しくは、以下の「静的ディスプレイ ID」をご覧ください。

履歴に関する理由により、設定は /data ディレクトリに保持されます。もともとは、ディスプレイの回転などのユーザー設定を保持するために使用されていました。

静的ディスプレイ ID

Android 9 以前では、フレームワーク内のディスプレイに固定 ID が提供されませんでした。ディスプレイがシステムに追加されると、静的カウンタを増加させることによりそのディスプレイに対して Display#mDisplayId または DisplayInfo#displayId が生成されました。システムに同じディスプレイを追加して削除しても、異なる ID が返されました。

起動できるディスプレイがデバイスに複数ある場合、タイミングによって異なる ID がディスプレイに割り当てられることがあります。Android 9 以前には DisplayInfo#uniqueId が含まれていましたが、物理ディスプレイは local:0 または local:1 として識別されて組み込みの外部ディスプレイを表していたため、ディスプレイを区別するのに十分な情報がありませんでした。

Android 10 では固定 ID を追加し、ローカル ディスプレイ、ネットワーク ディスプレイ、仮想ディスプレイを区別できるように DisplayInfo#uniqueId が変更されました。

ディスプレイの種類 形式
ローカル

local:<stable-id>
ネットワーク

network:<mac-address>
仮想

virtual:<package-name-and-name>

uniqueId の更新に加え、DisplayInfo.address には再起動後も変わらないディスプレイ ID である DisplayAddress が含まれています。Android 10 では、DisplayAddress は物理ディスプレイとネットワーク ディスプレイをサポートしています。DisplayAddress.Physical は固定ディスプレイ ID(uniqueId と同じ)を含み、DisplayAddress#fromPhysicalDisplayId() を使用して作成できます。

Android 10 には、ポート情報を取得するための便利なメソッドも用意されています(Physical#getPort())。このメソッドをフレームワークで使用して、ディスプレイを静的に識別できます。たとえば DisplayWindowSettings で使用します。DisplayAddress.Network は MAC アドレスを含み、DisplayAddress#fromMacAddress() で作成できます。

これらの追加により、デバイス メーカーは静的マルチディスプレイ設定でディスプレイを識別し、物理ディスプレイのポートなどの静的ディスプレイ ID を使用してさまざまなシステム設定と機能を設定できます。これらのメソッドは非表示になっており、system_server での使用のみを目的としています。

HWC ディスプレイ ID(不透明で固定されていない可能性がある)が指定されている場合、このメソッドによって、ディスプレイ出力の物理コネクタを識別する(プラットフォーム固有の)8 ビットのポート番号と、ディスプレイの EDID blob が返されます。SurfaceFlinger は、EDID からメーカーまたはモデルの情報を抽出して、フレームワークに公開される固定の 64 ビット ディスプレイ ID を生成します。このメソッドがサポートされていない、またはエラーが発生した場合、SurfaceFlinger は DisplayInfo#address が null で DisplayInfo#uniqueId がハードコードされている(前述)、以前の MD モードにフォールバックします。

この機能がサポートされていることを確認するには、以下を実行します。

    $ dumpsys SurfaceFlinger --display-id
    # Example output.
    Display 21691504607621632 (HWC display 0): port=0 pnpId=SHP displayName="LQ123P1JX32"
    Display 9834494747159041 (HWC display 2): port=1 pnpId=HWP displayName="HP Z24i"
    Display 1886279400700944 (HWC display 1): port=2 pnpId=AUS displayName="ASUS MB16AP"
    

ディスプレイごとのフォーカス

対象ディスプレイが異なる複数の入力ソースを同時にサポートするために、複数のフォーカス ウィンドウ(ディスプレイごとに 1 つまで)をサポートするよう Android 10 を設定できます。これは、複数のユーザーが同じデバイスを異なる入力方法を使用して同時に操作する特別なタイプのデバイス、または Android Automotive などのデバイスのみを対象としています。

この機能は、マルチスクリーン デバイスなどの通常のデバイスや、デスクトップのようなエクスペリエンスを目的に使用されるデバイスでは有効にしないことを強くおすすめします。これは、主にセキュリティ上の問題により、入力フォーカスされるウィンドウをユーザーが特定できなくなる可能性があるためです。

銀行アプリへのログインや、機密情報が含まれているテキストの入力など、ユーザーがテキスト入力フィールドにセキュアであるべき情報を入力するとします。悪意のあるアプリは、アクティビティを実行する、テキスト入力欄も含まれた仮想オフスクリーン ディスプレイを作成できます。正当なアクティビティと悪意のあるアクティビティにフォーカスする、どちらにも有効な入力インジケーター(点滅するカーソル)が表示されます。

ただし、キーボード(ハードウェアまたはソフトウェア)からの入力は最上位のアクティビティ(直近に起動されたアプリ)のみに入力されます。プライマリ デバイスのディスプレイでソフトウェア キーボードを使用しても、悪意のあるアプリは、非表示の仮想ディスプレイを作成してユーザー入力を取得できます。

ディスプレイごとのフォーカスを設定するには、com.android.internal.R.bool.config_perDisplayFocusEnabled を使用します。

互換性

問題: Android 9 以前のシステムでは、一度に 1 つのウィンドウのみフォーカスされます。

解決策: まれに同じプロセスの 2 つのウィンドウがフォーカスされる場合、Z オーダーの高いウィンドウのみフォーカスされます。この制限は、Android 10 をターゲットとするアプリでは削除されるため、同時にフォーカスされる複数のウィンドウに対応できることが想定されています。

実装

WindowManagerService#mPerDisplayFocusEnabled はこの機能の可用性を制御します。ActivityManagerActivityDisplay#getFocusedStack() は、変数でのグローバル トラッキングの代わりに使用されます。ActivityDisplay#getFocusedStack() は、値をキャッシュする代わりに Z オーダーに基づいてフォーカスを決定します。つまり、アクティビティの Z オーダーを追跡する必要があるのは 1 つのソース(WindowManager)のみです。

ActivityStackSupervisor#getTopDisplayFocusedStack() は、システムでフォーカスされる最上位のスタックを特定する必要がある場合に同様の方法を使用します。スタックが上から下に走査され、最初の有効なスタックが検索されます。

InputDispatcher は、フォーカスされる複数のウィンドウ(ディスプレイごとに 1 つ)を表示できるようになりました。ディスプレイ固有の入力イベントは、対応するディスプレイのフォーカスされるウィンドウにディスパッチされます。他の入力イベントは、フォーカスされるディスプレイ(ユーザーが最後に操作したディスプレイ)のフォーカスされるウィンドウにディスパッチされます。

InputDispatcher::mFocusedWindowHandlesByDisplayInputDispatcher::setFocusedDisplay() をご覧ください。NativeInputManager::setFocusedApplication() によって、フォーカスされるアプリも InputManagerService で個別に更新されます。

WindowManager では、フォーカスされるウィンドウも個別にトラッキングされます。DisplayContent#mCurrentFocusDisplayContent#mFocusedApp、およびそれぞれの使用法をご覧ください。関連するフォーカス トラッキングと更新のメソッドが WindowManagerService から DisplayContent に移動されました。