Displayunterstützung

Im Folgenden finden Sie die Änderungen, die an diesen anzeigenspezifischen Bereichen vorgenommen wurden:

Größe von Aktivitäten und Displays anpassen

Um anzugeben, dass eine App den Mehrfenstermodus oder die Größenänderung möglicherweise nicht unterstützt, verwenden Aktivitäten das Attribut resizeableActivity=false. Häufige Probleme, die bei Apps auftreten, wenn die Größe von Aktivitäten geändert wird:

  • Eine Aktivität kann eine andere Konfiguration als die App oder eine andere nicht visuelle Komponente haben. Ein häufiger Fehler ist das Lesen von Display-Messwerten aus dem App-Kontext. 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 möglicherweise die Größenänderung nicht verarbeiten und stürzt ab, zeigt eine verzerrte Benutzeroberfläche an oder verliert den Status aufgrund eines Neustarts, ohne den Instanzstatus zu speichern.
  • Eine App versucht möglicherweise, absolute Eingabekoordinaten zu verwenden (anstatt der Koordinaten relativ zur Fensterposition), was die Eingabe im Mehrfenstermodus beeinträchtigen kann.

In Android 7 und höher kann eine App so eingestellt werden, dass sie resizeableActivity=false immer im Vollbildmodus ausgeführt wird. In diesem Fall verhindert die Plattform, dass nicht anpassbare Aktivitäten in den Splitscreen-Modus wechseln. Wenn der Nutzer versucht, eine nicht anpassbare 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 anpassbare 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:

  • Dieselbe Konfiguration wird auf den Prozess angewendet, der alle Aktivitäten und Nicht-Aktivitätskomponenten enthält.
  • Die angewendete Konfiguration entspricht den CDD-Anforderungen für app-kompatible Displays.

In Android 10 verhindert die Plattform weiterhin, dass nicht anpassbare 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 deklariert wurde. Andernfalls wird die Aktivität so skaliert, dass sie den gesamten Bildschirm ausfüllt, wie in Android 9 und niedriger.

Bei der Standardimplementierung wird die folgende Richtlinie angewendet:

Wenn eine Aktivität, die durch die Verwendung des Attributs android:resizeableActivity als nicht mit dem Mehrfenstermodus kompatibel deklariert wurde, eine der unten beschriebenen Bedingungen erfüllt und sich die angewendete Bildschirmkonfiguration ändern muss, werden die Aktivität und der Prozess mit der ursprünglichen Konfiguration gespeichert. Der Nutzer erhält die Möglichkeit, den App-Prozess neu zu starten, um die aktualisierte Bildschirmkonfiguration zu verwenden.

  • Feste Ausrichtung über die Anwendung von android:screenOrientation
  • Die App hat ein standardmäßiges maximales oder minimales Seitenverhältnis, weil sie auf ein bestimmtes API-Level ausgerichtet ist oder das Seitenverhältnis explizit deklariert.

Diese Abbildung zeigt eine nicht anpassbare Aktivität mit einem deklarierten Seitenverhältnis. Wenn Sie das Gerät zusammenfalten, wird das Fenster so skaliert, dass es in den Bereich passt. Das Seitenverhältnis wird dabei durch entsprechendes Letterboxing beibehalten. Außerdem wird dem Nutzer jedes Mal, wenn sich der Anzeigebereich für die Aktivität ändert, die Option zum Neustarten der Aktivität angeboten.

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

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

Implementierung

Eine nicht anpassbare Aktivität mit fester Ausrichtung oder festem Seitenverhältnis wird im Code als „Größenkompatibilitätsmodus“ (Size Compatibility Mode, SCM) bezeichnet. Die Bedingung wird in ActivityRecord#shouldUseSizeCompatMode() definiert. Wenn eine SCM-Aktivität gestartet wird, ist die bildschirmbezogene Konfiguration (z. B. Größe oder Dichte) in der angeforderten Überschreibungskonfiguration festgelegt. Die Aktivität ist also nicht mehr von der aktuellen Displaykonfiguration abhängig.

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 für ihren Container verwendet wird (z. B. wenn die Anzeige skaliert oder die Aktivität auf einen anderen Bildschirm verschoben wird), ist ActivityRecord#inSizeCompatMode() „true“ und SizeCompatModeActivityController (in der System-UI) empfängt den Callback, um die Schaltfläche zum Neustarten des Prozesses anzuzeigen.

Displaygrößen und Seitenverhältnisse

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

Seitenverhältnisse von Apps in Android 10

Abbildung 1: Beispiele für in Android 10 unterstützte App-Verhältnisse

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

Apps können sich dafür registrieren, indem sie eine minimal unterstützte Größe deklarieren, die kleiner als oder gleich der Größe des Zieldisplays ist. Verwenden Sie dazu die Attribute für das Aktivitätslayout android:minHeight und android:minWidth im AndroidManifest.

Displayrichtlinien

In Android 10 werden bestimmte Displayrichtlinien von der Standardimplementierung WindowManagerPolicy in PhoneWindowManager getrennt und in klassenspezifische Klassen verschoben, z. B.:

  • Displaystatus und -ausrichtung
  • Einige Schlüssel und Bewegungsereignis-Tracking
  • System-UI und Dekorationsfenster

In Android 9 und niedriger wurden in der Klasse PhoneWindowManager unter anderem Richtlinien, Status und Einstellungen für das Display, die Drehung und die Verfolgung des Dekorationsfensterrahmens verarbeitet. In Android 10 wird der Großteil dieser Funktionen in die Klasse DisplayPolicy verschoben, mit Ausnahme der Rotationsverfolgung, die in DisplayRotation verschoben wurde.

Einstellungen für das Anzeigefenster

In Android 10 wurde die konfigurierbare Fensterverwaltungseinstellung pro Display erweitert und umfasst nun Folgendes:

  • Standardmäßiger Fenstermodus für die Anzeige
  • Overscan-Werte
  • Nutzerrotation und Rotationsmodus
  • Erzwungene Größe, Dichte und Skalierungsmodus
  • Modus für das Entfernen von Inhalten (wenn das Display entfernt wird)
  • Unterstützung für Systemdekorationen und IME

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

Unter Android 10 wird standardmäßig DisplayInfo#uniqueId als Kennung für ein Display verwendet, wenn die Einstellungen beibehalten werden. uniqueId sollte für alle Displays ausgefüllt werden. Außerdem ist es stabil für physische und Netzwerkdisplays. Es ist auch möglich, den Port eines physischen Displays als Kennung zu verwenden. Dieser kann in DisplayWindowSettings#mIdentifier festgelegt werden. Bei jedem Schreibvorgang werden alle Einstellungen geschrieben. Daher ist es sicher, den Schlüssel zu aktualisieren, der für einen Displayeintrag im Speicher verwendet wird. Weitere Informationen finden Sie unter Statische Display-IDs.

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

Statische Display-IDs

Unter Android 9 und niedriger gab es im Framework keine stabilen Kennungen für Displays. Wenn dem System ein Display hinzugefügt wurde, wurde für dieses Display durch Erhöhen eines statischen Zählers eine Display#mDisplayId oder DisplayInfo#displayId generiert. Wenn das System dasselbe Display hinzugefügt und entfernt hat, wurde eine andere ID zugewiesen.

Wenn ein Gerät von Anfang an mehrere Displays hatte, konnten den Displays je nach Timing unterschiedliche Kennungen zugewiesen werden. Android 9 (und früher) enthielt zwar DisplayInfo#uniqueId, aber nicht genügend Informationen, um zwischen Displays zu unterscheiden, da physische Displays entweder als local:0 oder local:1 identifiziert wurden, um das integrierte und das externe Display darzustellen.

In Android 10 wird DisplayInfo#uniqueId geändert, um einen stabilen Bezeichner 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 Aktualisierungen von uniqueId enthält DisplayInfo.address die DisplayAddress, eine Anzeige-ID, die bei Neustarts stabil bleibt. In Android 10 unterstützt DisplayAddress physische und Netzwerkdisplays. DisplayAddress.Physical enthält eine stabile Display-ID (dieselbe wie in uniqueId) und kann mit DisplayAddress#fromPhysicalDisplayId() erstellt werden.

Android 10 bietet auch eine praktische Methode zum Abrufen von Portinformationen (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 Setups mit mehreren Displays identifizieren und verschiedene Systemeinstellungen und Funktionen mithilfe statischer Display-IDs konfigurieren, z. B. Ports für physische Displays. Diese Methoden sind ausgeblendet und dürfen nur in system_server verwendet werden.

Bei einer HWC-Display-ID (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 die stabilen 64-Bit-Display-IDs zu generieren, die für das Framework verfügbar sind. Wenn diese Methode nicht unterstützt wird oder Fehler auftreten, greift SurfaceFlinger auf den alten MD-Modus zurück, in dem DisplayInfo#address null ist und DisplayInfo#uniqueId wie oben beschrieben fest codiert ist.

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 niedrigeren Versionen gingen SurfaceFlinger und DisplayManagerService davon aus, dass es höchstens zwei physische Displays mit den fest codierten IDs 0 und 1 gibt.

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

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

In Android 10 und niedriger ist das primäre interne Display TYPE_INTERNAL und alle sekundären Displays sind unabhängig vom Verbindungstyp als TYPE_EXTERNAL gekennzeichnet. Daher werden zusätzliche interne Displays als externe Displays behandelt. Als Workaround kann gerätespezifischer Code Annahmen zu DisplayAddress.Physical#getPort treffen, wenn der HWC bekannt ist und die Logik für die Portzuweisung vorhersehbar ist.

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

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

Implementierung

In Android 9 und niedriger werden Displays durch 32-Bit-IDs identifiziert. Dabei ist 0 das interne Display, 1 das externe Display, [2, INT32_MAX] HWC-Virtual-Displays und -1 ein ungültiges Display oder ein nicht HWC-Virtual-Display.

Ab Android 10 erhalten Displays stabile und dauerhafte IDs, sodass SurfaceFlinger und DisplayManagerService mehr als zwei Displays verfolgen und zuvor gesehene Displays erkennen können. Wenn der HWC IComposerClient.getDisplayIdentificationData unterstützt und Daten zur Display-Identifizierung bereitstellt, parst SurfaceFlinger die EDID-Struktur und weist stabile 64-Bit-Display-IDs für physische und virtuelle HWC-Displays zu. Die IDs werden mit einem Optionstyp ausgedrückt, wobei der Nullwert für ein ungültiges Display oder ein virtuelles Nicht-HWC-Display steht. Ohne HWC-Unterstützung greift SurfaceFlinger auf das Legacy-Verhalten mit maximal zwei physischen Displays 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 es mehrere fokussierte Fenster unterstützt, maximal eines pro Display. Dies ist nur für spezielle Gerätetypen vorgesehen, wenn mehrere Nutzer gleichzeitig mit demselben Gerät interagieren und verschiedene Eingabemethoden oder Geräte verwenden, z. B. Android Automotive.

Es wird dringend empfohlen, diese Funktion nicht für reguläre Geräte zu aktivieren, einschließlich Geräte mit mehreren Bildschirmen oder Geräte, die für desktopähnliche Anwendungen verwendet werden. Das liegt vor allem an einem Sicherheitsrisiko, das dazu führen kann, dass Nutzer sich fragen, welches Fenster den Eingabefokus hat.

Stellen Sie sich einen Nutzer vor, der vertrauliche Informationen in ein Texteingabefeld eingibt, z. B. um sich in einer Banking-App anzumelden oder Text mit sensiblen Informationen einzugeben. Eine schädliche App könnte einen virtuellen Off-Screen-Bildschirm erstellen, auf dem eine Aktivität ausgeführt wird, auch mit einem Texteingabefeld. Sowohl bei legitimen als auch bei schädlichen Aktivitäten wird der Fokus angezeigt und es ist ein aktiver Eingabeindikator (blinkender Cursor) zu sehen.

Da Eingaben über eine Tastatur (Hardware oder Software) jedoch nur in die oberste Aktivität (die zuletzt gestartete App) eingegeben werden, könnte eine schädliche App durch das Erstellen eines verborgenen virtuellen Displays Nutzereingaben abfangen, auch wenn eine Softwaretastatur auf dem primären Gerätedisplay verwendet wird.

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

Kompatibilität

Problem:Unter Android 9 und niedriger kann jeweils nur ein Fenster im System den Fokus haben.

Lösung:In dem seltenen Fall, dass zwei Fenster aus demselben Prozess fokussiert werden, stellt das System den Fokus nur dem Fenster zur Verfügung, das in der Z-Reihenfolge höher steht. Diese Einschränkung wird für Apps entfernt, die auf Android 10 ausgerichtet sind. Es wird erwartet, dass sie mehrere Fenster unterstützen, die gleichzeitig im Fokus stehen.

Implementierung

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

ActivityStackSupervisor#getTopDisplayFocusedStack() geht ähnlich vor, wenn der oberste fokussierte Stack im System identifiziert werden muss. Die Stapel werden von oben nach unten durchlaufen, um den ersten infrage kommenden Stapel zu finden.

InputDispatcher kann jetzt mehrere fokussierte Fenster haben (eines pro Display). Wenn ein Eingabeereignis displayspezifisch ist, wird es an das fokussierte Fenster auf dem entsprechenden Display gesendet. Andernfalls wird sie an das fokussierte Fenster auf dem fokussierten Display gesendet. Das ist das Display, mit dem der Nutzer zuletzt interagiert hat.

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

In WindowManager werden fokussierte Fenster ebenfalls separat erfasst. Weitere Informationen finden Sie unter DisplayContent#mCurrentFocus und DisplayContent#mFocusedApp. Die zugehörigen Methoden zum Verfolgen und Aktualisieren des Fokus wurden von WindowManagerService nach DisplayContent verschoben.