Displayunterstützung

Im Folgenden finden Sie die Änderungen an diesen anzeigespezifischen Bereichen:

Größe von Aktivitäten und Displays anpassen

Wenn eine App möglicherweise keinen Multifenstermodus oder keine Größenänderung unterstützt, wird in Aktivitäten das Attribut resizeableActivity=false verwendet. Häufige Probleme bei Apps, wenn Aktivitäten die Größe ändern:

  • Eine Aktivität kann eine andere Konfiguration als die App oder eine andere nicht visuelle Komponente haben. Ein häufiger Fehler ist es, Displaymesswerte aus dem App-Kontext zu lesen. Die zurückgegebenen Werte werden nicht an die Messwerte für den sichtbaren Bereich angepasst, in dem eine Aktivität angezeigt wird.
  • Eine Aktivität kann das Ändern der Größe möglicherweise nicht verarbeiten und abstürzen, eine verzerrte Benutzeroberfläche anzeigen oder den Status verlieren, weil sie neu gestartet wird, ohne den Instanzstatus zu speichern.
  • Eine Anwendung kann versuchen, absolute Eingabekoordinaten zu verwenden (anstelle von Koordinaten relativ zur Fensterposition), was die Eingabe im Mehrfenstermodus beeinträchtigen kann.

Unter Android 7 und höher kann eine App soresizeableActivity=false eingerichtet werden, dass sie immer im Vollbildmodus ausgeführt wird. In diesem Fall verhindert die Plattform, dass nicht skalierbare Aktivitäten im Splitscreen angezeigt werden. Wenn der Nutzer versucht, eine nicht veränderbare Aktivität über den Launcher aufzurufen, während er sich bereits im Splitscreen-Modus befindet, beendet die Plattform den Splitscreen-Modus und startet die nicht veränderbare Aktivität im Vollbildmodus.

Apps, die dieses Attribut im Manifest explizit auf false setzen, dürfen nicht im Mehrfenstermodus gestartet werden, es sei denn, der Kompatibilitätsmodus wird angewendet:

  • Die gleiche Konfiguration wird auf den Prozess angewendet, der alle Aktivitäten und nicht aktivitätsbezogenen Komponenten enthält.
  • Die angewendete Konfiguration erfüllt die CDD-Anforderungen für App-kompatible Displays.

Unter Android 10 verhindert die Plattform weiterhin, dass nicht skalierbare Aktivitäten in den Splitscreen-Modus wechseln. Sie können jedoch vorübergehend skaliert werden, wenn für die Aktivität eine feste Ausrichtung oder ein festes Seitenverhältnis festgelegt wurde. Andernfalls wird die Aktivität so skaliert, dass sie den gesamten Bildschirm ausfüllt, wie unter Android 9 und niedriger.

Bei der Standardimplementierung gilt die folgende Richtlinie:

Wenn eine Aktivität mithilfe des Attributs android:resizeableActivity als nicht mit dem Multifenstermodus kompatibel erklärt wurde und diese Aktivität eine der unten beschriebenen Bedingungen erfüllt, muss sich die angewendete Bildschirmkonfiguration ändern. Die Aktivität und der Prozess werden dann mit der ursprünglichen Konfiguration gespeichert und der Nutzer kann den App-Prozess neu starten, um die aktualisierte Bildschirmkonfiguration zu verwenden.

  • Ist die Ausrichtung durch die Anwendung von android:screenOrientation festgelegt?
  • Die App hat ein standardmäßiges maximales oder minimales Seitenverhältnis, da sie auf API-Level ausgerichtet ist oder das Seitenverhältnis explizit deklariert.

Diese Abbildung zeigt eine nicht skalierbare Aktivität mit einem angegebenen Seitenverhältnis. Wenn das Gerät zusammengeklappt wird, wird das Fenster so skaliert, dass es in den Bereich passt. Dabei wird das Seitenverhältnis mithilfe des entsprechenden Letterboxing beibehalten. Außerdem wird dem Nutzer jedes Mal, wenn der Anzeigebereich für die Aktivität geändert wird, die Option zum Neustarten der Aktivität angezeigt.

Wenn Sie das Gerät aufklappen, ändern sich Konfiguration, Größe und Seitenverhältnis der Aktivität nicht, aber die Option zum Neustarten der Aktivität wird angezeigt.

Wenn resizeableActivity nicht festgelegt oder auf true gesetzt ist, unterstützt die App die Größenanpassung vollständig.

Implementierung

Eine nicht skalierbare Aktivität mit fester Ausrichtung oder festem Seitenverhältnis wird im Code als „SCM“ (Size Compatibility Mode) bezeichnet. Die Bedingung ist in ActivityRecord#shouldUseSizeCompatMode() definiert. Beim Start einer SCM-Aktivität wird die bildschirmbezogene Konfiguration (z. B. Größe oder Dichte) in der angeforderten Überschreibungskonfiguration festgelegt. Die Aktivität hängt also nicht mehr von der aktuellen Anzeigekonfiguration ab.

Wenn die SCM-Aktivität nicht den gesamten Bildschirm ausfüllen kann, wird sie oben ausgerichtet und horizontal zentriert. Die Aktivitätsgrenzen werden von AppWindowToken#calculateCompatBoundsTransformation() berechnet.

Wenn für eine SCM-Aktivität eine andere Bildschirmkonfiguration als die des Containers verwendet wird (z. B. wenn die Größe des Displays geändert oder die Aktivität auf ein anderes Display verschoben wird), ist ActivityRecord#inSizeCompatMode() wahr und SizeCompatModeActivityController (in der System-UI) erhält den Rückruf, um die Schaltfläche zum Neustarten des Prozesses anzuzeigen.

Displaygrößen und Seitenverhältnisse

Android 10 unterstützt neue Seitenverhältnisse, von hohen Seitenverhältnissen bei langen und schmalen Displays bis hin zu 1:1. Apps können die ApplicationInfo#maxAspectRatio und die ApplicationInfo#minAspectRatio des Bildschirms definieren, die sie verarbeiten können.

App-Verhältnisse in Android 10

Abbildung 1. Beispiel für App-Verhältnisse, die unter Android 10 unterstützt werden

Geräteimplementierungen können sekundäre Bildschirme mit Größen und Auflösungen haben, die kleiner als die für Android 9 erforderlichen und niedrigeren (mindestens 2,5 Zoll Breite oder Höhe, mindestens 320 DP für smallestScreenWidth) sind.Es dürfen jedoch nur Aktivitäten platziert werden, die diese kleinen Displays unterstützen.

Sie können die Funktion aktivieren, indem Sie eine Mindestgröße angeben, die kleiner als die Zieldisplaygröße ist oder mit dieser übereinstimmt. Verwenden Sie dazu die Aktivitätslayoutattribute android:minHeight und android:minWidth im AndroidManifest.

Displayanzeigenrichtlinien

In Android 10 werden bestimmte Anzeigerichtlinien von der standardmäßigen WindowManagerPolicy-Implementierung in PhoneWindowManager in Klassen pro Display getrennt und verschoben. Dazu gehören:

  • Displaystatus und -ausrichtung
  • Tracking einiger Tasten und Bewegungsereignisse
  • System-UI und Dekorationsfenster

In Android 9 (und niedriger) wurde die Klasse PhoneWindowManager für Anzeigerichtlinien, den Status und die Einstellungen, die Drehung, das Tracking von Weihnachtsfensterrahmen und mehr verwendet. Android 10 verschiebt den Großteil davon in die DisplayPolicy-Klasse, mit Ausnahme des Rotationstrackings, das in DisplayRotation verschoben wurde.

Einstellungen für das Anzeigefenster

In Android 10 wurde die konfigurierbare Einstellung für die Fenstergröße pro Display um folgende Optionen erweitert:

  • Standardfenstermodus
  • Werte für Overscan
  • Nutzerrotation und Rotationsmodus
  • Erzwungene Größe, Dichte und Skalierungsmodus
  • Modus zur Entfernung von Inhalten (wenn das Display entfernt wird)
  • Unterstützung für Systemdekorationen und IME

Die Klasse DisplayWindowSettings enthält Einstellungen für diese Optionen. Sie werden jedes Mal, wenn eine Einstellung geändert wird, auf der Festplatte in der Partition /data in display_settings.xml gespeichert. Weitere Informationen finden Sie unter DisplayWindowSettings.AtomicFileStorage und DisplayWindowSettings#writeSettings(). Gerätehersteller können in display_settings.xml Standardwerte für ihre Gerätekonfiguration angeben. Da die Datei jedoch in /data gespeichert ist, ist möglicherweise zusätzliche Logik erforderlich, um sie wiederherzustellen, wenn sie durch ein Löschen gelöscht wurde.

In Android 10 wird standardmäßig DisplayInfo#uniqueId als Kennung für ein Display verwendet, wenn die Einstellungen gespeichert werden. uniqueId muss für alle Displays ausgefüllt sein. Außerdem ist er für physische und Netzwerkbildschirme stabil. Sie können auch den Port eines physischen Bildschirms als Kennung verwenden, die in DisplayWindowSettings#mIdentifier festgelegt werden kann. Bei jedem Schreibvorgang werden alle Einstellungen geschrieben. Daher kann der Schlüssel, der für einen Displayeintrag verwendet wird, sicher im Speicher aktualisiert werden. Weitere Informationen finden Sie unter Statische Anzeige-IDs.

Einstellungen werden aus historischen Gründen im Verzeichnis /data gespeichert. Ursprünglich wurden sie verwendet, um von Nutzern festgelegte Einstellungen wie die Displaydrehung beizubehalten.

Static display identifiers

Android 9 (und niedriger) bot keine stabilen IDs für Displays im Framework. Wenn dem System ein Display hinzugefügt wurde, wurde Display#mDisplayId oder DisplayInfo#displayId für dieses Display generiert, indem ein statischer Zähler erhöht wurde. Wenn das System dasselbe Display hinzugefügt und wieder entfernt hat, wurde eine andere ID zugewiesen.

Wenn bei einem Gerät beim Starten mehrere Displays verfügbar sind, können den Displays je nach Zeitpunkt unterschiedliche IDs zugewiesen werden. Android 9 (und niedriger) enthielt zwar DisplayInfo#uniqueId, aber nicht genügend Informationen, um zwischen Displays zu unterscheiden, da physische Displays entweder als local:0 oder local:1 gekennzeichnet wurden, um das integrierte und das externe Display zu repräsentieren.

Unter Android 10 wurde DisplayInfo#uniqueId geändert, um eine stabile Kennung hinzuzufügen und zwischen lokalen, Netzwerk- und virtuellen Displays zu unterscheiden.

Displaytyp Format
Lokal
local:<stable-id>
Netz
network:<mac-address>
Virtuell
virtual:<package-name-and-name>

Zusätzlich zu den Aktualisierungen von uniqueId enthält DisplayInfo.address DisplayAddress, eine Display-ID, die nach einem Neustart unverändert bleibt. In Android 10 unterstützt DisplayAddress physische Displays und Netzwerkbildschirme. DisplayAddress.Physical enthält eine stabile Anzeigen-ID (wie in uniqueId) und kann mit DisplayAddress#fromPhysicalDisplayId() erstellt werden.

Android 10 bietet auch eine praktische Methode zum Abrufen von Anschlussinformationen (Physical#getPort()). Diese Methode kann im Framework verwendet werden, um Displays statisch zu identifizieren. Sie wird beispielsweise in DisplayWindowSettings verwendet. DisplayAddress.Network enthält die MAC-Adresse und kann mit DisplayAddress#fromMacAddress() erstellt werden.

Mit diesen Ergänzungen können Gerätehersteller Displays in statischen Mehrfach-Display-Konfigurationen identifizieren und verschiedene Systemeinstellungen und -funktionen mithilfe statischer Display-IDs wie Ports für physische Displays konfigurieren. Diese Methoden sind ausgeblendet und dürfen nur innerhalb von system_server verwendet werden.

Wenn eine HWC-Display-ID angegeben wird (die undurchsichtig und nicht immer stabil sein kann), gibt diese Methode die (plattformspezifische) 8‑Bit-Portnummer zurück, die einen physischen Anschluss für die Displayausgabe identifiziert, sowie den EDID-Blob des Displays. SurfaceFlinger extrahiert Hersteller- oder Modellinformationen aus der EDID, um stabile 64‑Bit-Display-IDs zu generieren, die für das Framework freigegeben werden. Wenn diese Methode nicht unterstützt wird oder Fehler auftritt, greift SurfaceFlinger auf den alten MD-Modus zurück, in dem DisplayInfo#address null und DisplayInfo#uniqueId hartcodiert ist, wie oben beschrieben.

So prüfen Sie, ob diese Funktion unterstützt wird:

$ 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"

Mehr als zwei Displays verwenden

In Android 9 (und niedriger) gingen SurfaceFlinger und DisplayManagerService davon aus, dass maximal zwei physische Displays mit den hartcodierten IDs 0 und 1 vorhanden sind.

Ab Android 10 konnte SurfaceFlinger eine Hardware Composer API (HWC) nutzen, um stabile Display-IDs zu generieren, wodurch eine beliebige Anzahl physischer Displays verwaltet werden konnte. Weitere Informationen finden Sie unter Statische Anzeigen-IDs.

Das Framework kann das IBinder-Token für ein physisches Display über SurfaceControl#getPhysicalDisplayToken abrufen, nachdem es die 64‑Bit-Display-ID von SurfaceControl#getPhysicalDisplayIds oder aus einem DisplayEventReceiver-Hotplug-Ereignis abgerufen hat.

Unter Android 10 (und niedriger) ist das primäre interne Display TYPE_INTERNAL und alle sekundären Displays werden unabhängig vom Verbindungstyp als TYPE_EXTERNAL gekennzeichnet. Daher werden zusätzliche interne Displays als extern behandelt. Als Problemumgehung können in Gerätecode Annahmen über DisplayAddress.Physical#getPort getroffen werden, wenn der HWC bekannt ist und die Logik der Portzuweisung vorhersehbar ist.

Diese Einschränkung wurde in Android 11 und höher aufgehoben.

  • In Android 11 ist das erste Display, das beim Start gemeldet wird, das primäre Display. Der Verbindungstyp (intern oder extern) ist irrelevant. Das primäre Display kann jedoch nicht getrennt werden, sodass es sich in der Praxis um ein internes Display handeln muss. Einige faltbare Smartphones haben mehrere interne Displays.
  • Sekundäre Anzeigen werden je nach Verbindungstyp korrekt als Display.TYPE_INTERNAL oder Display.TYPE_EXTERNAL (früher Display.TYPE_BUILT_IN und Display.TYPE_HDMI) kategorisiert.

Implementierung

Unter Android 9 und niedriger werden Displays anhand von 32‑Bit-IDs identifiziert. Dabei steht 0 für das interne Display, 1 für das externe Display, [2, INT32_MAX] für virtuelle HWC-Displays und -1 für ein ungültiges Display oder ein virtuelles Display ohne HWC.

Ab Android 10 werden Displays stabile und dauerhafte IDs zugewiesen. Dadurch können SurfaceFlinger und DisplayManagerService mehr als zwei Displays erfassen und zuvor gesehene Displays erkennen. Wenn die HWC IComposerClient.getDisplayIdentificationData unterstützt und Anzeige-Identifikationsdaten bereitstellt, parst SurfaceFlinger die EDID-Struktur und weist stabile 64-Bit-Anzeige-IDs für physische und virtuelle HWC-Displays zu. Die IDs werden mit einem Optionstyp angegeben, wobei der Nullwert für ein ungültiges Display oder ein virtuelles Display ohne HWC steht. Ohne HWC-Unterstützung greift SurfaceFlinger auf das Legacy-Verhalten von höchstens zwei physischen Bildschirmen zurück.

Fokus pro Display

Um mehrere Eingabequellen zu unterstützen, die gleichzeitig auf einzelne Displays ausgerichtet sind, kann Android 10 so konfiguriert werden, dass mehrere fokussierte Fenster unterstützt werden, maximal eines pro Display. Diese Funktion ist nur für spezielle Gerätetypen vorgesehen, bei denen mehrere Nutzer gleichzeitig mit demselben Gerät interagieren und dabei unterschiedliche Eingabemethoden oder Geräte verwenden, z. B. Android Automotive.

Wir empfehlen dringend, diese Funktion nicht für normale Geräte zu aktivieren, einschließlich Geräte mit mehreren Bildschirmen oder Geräten, die für eine Desktop-ähnliche Nutzung verwendet werden. Dies ist in erster Linie auf Sicherheitsbedenken zurückzuführen, durch die sich Nutzer möglicherweise fragen, welches Fenster einen Eingabefokus hat.

Stellen Sie sich den Nutzer vor, der sichere Informationen in ein Texteingabefeld eingibt, z. B. sich in einer Bank-App anmeldet oder Text mit vertraulichen Informationen eingibt. Eine schädliche App kann ein virtuelles Off-Screen-Display erstellen, mit dem eine Aktivität ausgeführt wird, auch mit einem Texteingabefeld. Sowohl legitime als auch schädliche Aktivitäten sind im Fokus und beide zeigen einen aktiven Eingabeindikator (blinkenden Cursor) an.

Da Eingaben über eine Tastatur (Hardware oder Software) jedoch nur in die oberste Aktivität (die zuletzt gestartete App) einfließen, kann eine schädliche App durch die Erstellung einer verborgenen virtuellen Anzeige Nutzereingaben erfassen, selbst wenn sie eine Softwaretastatur auf dem primären Bildschirm des Geräts verwenden.

Mit com.android.internal.R.bool.config_perDisplayFocusEnabled können Sie den Fokus für jedes Display festlegen.

Kompatibilität

Problem: Bei Android 9 und niedriger hat jeweils nur ein Fenster im System den Fokus.

Lösung:In dem seltenen Fall, dass zwei Fenster desselben Prozesses im Fokus sind, legt das System den Fokus nur auf das Fenster, das in der Z-Reihenfolge höher ist. Diese Einschränkung gilt nicht für Apps, die auf Android 10 ausgerichtet sind. Bei diesen Apps kann dann erwartet werden, dass mehrere Fenster gleichzeitig im Fokus sind.

Implementierung

WindowManagerService#mPerDisplayFocusEnabled steuert die Verfügbarkeit dieser Funktion. In ActivityManager wird jetzt ActivityDisplay#getFocusedStack() anstelle von globalem Tracking in einer Variablen verwendet. ActivityDisplay#getFocusedStack() legt den Fokus anhand der Z-Reihenfolge fest, anstatt den Wert im Cache zu speichern. So muss nur eine Quelle, WindowManager, die Z-Reihenfolge der Aktivitäten verfolgen.

ActivityStackSupervisor#getTopDisplayFocusedStack() verwendet einen ähnlichen Ansatz für Fälle, in denen der oberste Stack im System identifiziert werden muss. Die Stapel werden von oben nach unten durchsucht, um den ersten geeigneten Stapel zu finden.

InputDispatcher kann jetzt mehrere Fenster im Fokus haben (jeweils eines pro Bildschirm). Wenn ein Eingabeereignis displayspezifisch ist, wird es an das Fenster mit der Fokussierung auf dem entsprechenden Display gesendet. Andernfalls wird die Benachrichtigung an das Fenster auf dem Display gesendet, mit dem der Nutzer zuletzt interagiert hat.

Weitere Informationen finden Sie unter InputDispatcher::mFocusedWindowHandlesByDisplay und InputDispatcher::setFocusedDisplay(). Aktive Apps werden auch separat im InputManagerService über NativeInputManager::setFocusedApplication() aktualisiert.

In WindowManager werden auch Fenster, die gerade aktiv sind, separat erfasst. Weitere Informationen finden Sie unter DisplayContent#mCurrentFocus und DisplayContent#mFocusedApp sowie die jeweiligen Verwendungszwecke. Die zugehörigen Methoden zum Fokus-Tracking und -Aktualisieren wurden von WindowManagerService zu DisplayContent verschoben.