Supporto dei display

Di seguito sono riportati gli aggiornamenti apportati a queste aree specifiche per il display:

Ridimensionare attività e display

Per indicare che un'app potrebbe non supportare la modalità multi-finestra o il ridimensionamento, le attività utilizzano l'attributo resizeableActivity=false. I problemi comuni riscontrati dalle app quando le attività vengono ridimensionate includono:

  • Un'attività può avere una configurazione diversa dall'app o da un altro componente non visivo. Un errore comune è leggere le metriche di visualizzazione dal contesto dell'app. I valori restituiti non verranno aggiustati in base alle metriche dell'area visibile in cui viene visualizzata un'attività.
  • Un'attività potrebbe non gestire il ridimensionamento e arrestarsi in modo anomalo, visualizzare un'interfaccia utente distorta o perdere lo stato a causa del riavvio senza salvare lo stato dell'istanza.
  • Un'app potrebbe tentare di utilizzare coordinate di input assolute (anziché quelle relative alla posizione della finestra), il che potrebbe interrompere l'input in modalità multi-finestra.

In Android 7 (e versioni successive), un'app può essere impostata resizeableActivity=false per essere sempre eseguita in modalità a schermo intero. In questo caso, la piattaforma impedisce alle attività non ridimensionabili di passare alla modalità schermo diviso. Se l'utente tenta di richiamare un'attività non ridimensionabile dal launcher mentre è già in modalità schermo diviso, la piattaforma esce dalla modalità schermo diviso e avvia l'attività non ridimensionabile in modalità a schermo intero.

Le app che impostano esplicitamente questo attributo su false nel manifest non devono essere avviate in modalità multi-finestra, a meno che non venga applicata la modalità di compatibilità:

  • Al processo viene applicata la stessa configurazione, che contiene tutte le attività e i componenti non attività.
  • La configurazione applicata soddisfa i requisiti CDD per i display compatibili con le app.

In Android 10, la piattaforma impedisce ancora alle attività non ridimensionabili di passare alla modalità Split Screen, ma possono essere ridimensionate temporaneamente se l'attività ha dichiarato un orientamento o un formato fisso. In caso contrario, l'attività viene ridimensionata per riempire l'intero schermo come in Android 9 e versioni precedenti.

L'implementazione predefinita applica le seguenti norme:

Quando un'attività dichiarata incompatibile con il multi-window tramite l'utilizzo dell'attributo android:resizeableActivity e quando questa attività soddisfa una delle condizioni descritte di seguito, quando la configurazione dello schermo applicata deve cambiare, l'attività e il processo vengono salvati con la configurazione originale e all'utente viene fornita un'opportunità per riavviare il processo dell'app per utilizzare la configurazione dello schermo aggiornata.

  • È l'orientamento fisso tramite l'applicazione di android:screenOrientation
  • L'app ha proporzioni massime o minime predefinite scegliendo come target un livello API o dichiara le proporzioni in modo esplicito

Questa figura mostra un'attività non ridimensionabile con proporzioni dichiarate. Quando pieghi il dispositivo, la finestra viene ridimensionata per adattarsi all'area mantenendo le proporzioni utilizzando il letterbox appropriato. Inoltre, all'utente viene fornita un'opzione di riavvio dell'attività ogni volta che viene modificata l'area di visualizzazione dell'attività.

Quando apri il dispositivo, la configurazione, le dimensioni e le proporzioni dell'attività non cambiano, ma viene visualizzata l'opzione per riavviarla.

Quando resizeableActivity non è impostato (o è impostato su true), l'app supporta completamente il ridimensionamento.

Implementazione

Un'attività non ridimensionabile con orientamento o proporzioni fissi è denominata modalità di compatibilità delle dimensioni (SCM) nel codice. La condizione è definita in ActivityRecord#shouldUseSizeCompatMode(). Quando viene avviata un'attività SCM, la configurazione relativa allo schermo (come dimensioni o densità) viene corretta nella configurazione di override richiesta, quindi l'attività non dipende più dalla configurazione di visualizzazione corrente.

Se l'attività SCM non può riempire l'intero schermo, viene allineata in alto e centrata orizzontalmente. I limiti dell'attività vengono calcolati da AppWindowToken#calculateCompatBoundsTransformation().

Quando un'attività SCM utilizza una configurazione dello schermo diversa dal relativo contenitore (ad esempio, il display viene ridimensionato o l'attività viene spostata su un altro display), ActivityRecord#inSizeCompatMode() è true e SizeCompatModeActivityController (nell'interfaccia utente di sistema) riceve il callback per mostrare il pulsante di riavvio del processo.

Dimensioni e proporzioni del display

Android 10 supporta nuovi formati dalle proporzioni elevate di schermi lunghi e sottili a proporzioni 1:1. Le app possono definire ApplicationInfo#maxAspectRatio e il ApplicationInfo#minAspectRatio dello schermo che sono in grado di gestire.

Proporzioni delle app in Android 10

Figura 1. Esempio di proporzioni delle app supportate in Android 10

Le implementazioni dei dispositivi possono avere display secondari con dimensioni e risoluzioni inferiori a quelle richieste da Android 9 e versioni precedenti (minimo 2, 5 pollici di larghezza o altezza, minimo 320 DP per smallestScreenWidth), ma solo le attività che accettano di supportare questi piccoli display possono essere posizionate lì.

Le app possono attivare questa funzionalità dichiarando una dimensione minima supportata inferiore o uguale alla dimensione del display di destinazione. Utilizza gli attributi di layout dell'attività android:minHeight e android:minWidth in AndroidManifest.xml.

Norme per gli annunci display

Android 10 separa e sposta alcuni criteri di visualizzazione dall'implementazione WindowManagerPolicy predefinita in PhoneWindowManager a classi per display, ad esempio:

  • Stato e rotazione del display
  • Alcuni tasti e il monitoraggio degli eventi di movimento
  • Finestre di decorazione e UI di sistema

In Android 9 (e versioni precedenti), la classe PhoneWindowManager gestiva le norme di visualizzazione, lo stato e le impostazioni, la rotazione, il monitoraggio del frame della finestra di decorazione e altro ancora. Android 10 sposta la maggior parte di questi elementi nella classe DisplayPolicy, ad eccezione del monitoraggio della rotazione, che è stato spostato in DisplayRotation.

Impostazioni della finestra di visualizzazione

In Android 10, l'impostazione di finestre configurabile per display è stata ampliata per includere:

  • Modalità di visualizzazione predefinita
  • Valori di overscan
  • Rotazione degli utenti e modalità di rotazione
  • Modalità di ridimensionamento, densità e dimensioni forzate
  • Modalità di rimozione dei contenuti (quando viene rimosso il display)
  • Supporto per decorazioni di sistema e IME

La classe DisplayWindowSettings contiene le impostazioni per queste opzioni. Vengono salvati su disco nella partizione /data in display_settings.xml ogni volta che viene modificata un'impostazione. Per i dettagli, vedi DisplayWindowSettings.AtomicFileStorage e DisplayWindowSettings#writeSettings(). I produttori di dispositivi possono fornire valori predefiniti in display_settings.xml per la configurazione del dispositivo. Tuttavia, poiché il file è archiviato in /data, potrebbe essere necessaria una logica aggiuntiva per ripristinarlo se è stato cancellato da una pulizia.

Per impostazione predefinita, Android 10 utilizza DisplayInfo#uniqueId come identificatore per un display durante il salvataggio delle impostazioni. uniqueId deve essere compilato per tutti i display. Inoltre, è stabile per i display fisici e di rete. È anche possibile utilizzare la porta di un display fisico come identificatore, che può essere impostato in DisplayWindowSettings#mIdentifier. A ogni scrittura, tutte le impostazioni vengono scritte, quindi è sicuro aggiornare la chiave utilizzata per una voce di visualizzazione nello spazio di archiviazione. Per maggiori dettagli, vedi Identificatori di visualizzazione statici.

Le impostazioni vengono mantenute nella directory /data per motivi storici. In origine, venivano utilizzate per conservare le impostazioni impostate dall'utente, ad esempio la rotazione del display.

Identificatori di visualizzazione statica

Android 9 (e versioni precedenti) non forniva identificatori stabili per i display nel framework. Quando un display è stato aggiunto al sistema, Display#mDisplayId o DisplayInfo#displayId è stato generato per quel display incrementando un contatore statico. Se il sistema ha aggiunto e rimosso lo stesso display, è stato generato un ID diverso.

Se un dispositivo aveva più display disponibili all'avvio, ai display potrebbero essere assegnati identificatori diversi, a seconda della tempistica. Sebbene Android 9 (e versioni precedenti) includa DisplayInfo#uniqueId, non contiene informazioni sufficienti per distinguere i display perché i display fisici sono stati identificati come local:0 o local:1, per rappresentare il display integrato ed esterno.

Android 10 modifica DisplayInfo#uniqueId per aggiungere un identificatore stabile e distinguere tra display locali, di rete e virtuali.

Tipo di display Formato
Locale
local:<stable-id>
Rete
network:<mac-address>
Virtuale
virtual:<package-name-and-name>

Oltre agli aggiornamenti di uniqueId, DisplayInfo.address contiene DisplayAddress, un identificatore del display stabile durante i riavvii. In Android 10, DisplayAddress supporta display fisici e di rete. DisplayAddress.Physical contiene un ID display stabile (lo stesso di uniqueId) e può essere creato con DisplayAddress#fromPhysicalDisplayId().

Android 10 fornisce anche un metodo pratico per ottenere informazioni sulla porta (Physical#getPort()). Questo metodo può essere utilizzato nel framework per identificare staticamente i display. Ad esempio, viene utilizzato in DisplayWindowSettings). DisplayAddress.Network contiene l'indirizzo MAC e può essere creato con DisplayAddress#fromMacAddress().

Queste aggiunte consentono ai produttori di dispositivi di identificare i display nelle configurazioni statiche multi-display e di configurare diverse impostazioni e funzionalità di sistema utilizzando identificatori di display statici, come le porte per i display fisici. Questi metodi sono nascosti e sono destinati all'uso solo all'interno di system_server.

Dato un ID display HWC (che può essere opaco e non sempre stabile), questo metodo restituisce il numero di porta a 8 bit (specifico della piattaforma) che identifica un connettore fisico per l'output del display, nonché il blob EDID del display. SurfaceFlinger estrae le informazioni su produttore o modello dall'EDID per generare gli ID display stabili a 64 bit esposti al framework. Se questo metodo non è supportato o genera errori, SurfaceFlinger torna alla modalità MD legacy, in cui DisplayInfo#address è null e DisplayInfo#uniqueId è hardcoded, come descritto in precedenza.

Per verificare che questa funzionalità sia supportata, esegui:

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

Utilizzare più di due display

In Android 9 (e versioni precedenti), SurfaceFlinger e DisplayManagerService presupponevano l'esistenza di al massimo due display fisici con ID hardcoded 0 e 1.

A partire da Android 10, SurfaceFlinger può utilizzare un'API Hardware Composer (HWC) per generare ID display stabili, il che gli consente di gestire un numero arbitrario di display fisici. Per scoprire di più, consulta Identificatori display statici.

Il framework può cercare il token IBinder per un display fisico tramite SurfaceControl#getPhysicalDisplayToken dopo aver ottenuto l'ID display a 64 bit da SurfaceControl#getPhysicalDisplayIds o da un evento hotplug DisplayEventReceiver.

In Android 10 (e versioni precedenti), il display interno principale è TYPE_INTERNAL e tutti i display secondari sono contrassegnati come TYPE_EXTERNAL indipendentemente dal tipo di connessione. Pertanto, i display interni aggiuntivi vengono trattati come esterni. Come soluzione alternativa, il codice specifico del dispositivo può fare ipotesi su DisplayAddress.Physical#getPort se l'HWC è noto e la logica di allocazione delle porte è prevedibile.

Questa limitazione viene rimossa in Android 11 (e versioni successive).

  • In Android 11, il primo display segnalato durante l'avvio è il display principale. Il tipo di connessione (interna o esterna) è irrilevante. Tuttavia, rimane vero che il display principale non può essere disconnesso e di conseguenza deve essere un display interno. Tieni presente che alcuni smartphone pieghevoli hanno più display interni.
  • I display secondari sono classificati correttamente come Display.TYPE_INTERNAL o Display.TYPE_EXTERNAL (precedentemente noti come Display.TYPE_BUILT_IN e Display.TYPE_HDMI, rispettivamente) a seconda del tipo di connessione.

Implementazione

In Android 9 e versioni precedenti, i display vengono identificati da ID a 32 bit, dove 0 è il display interno, 1 è il display esterno, [2, INT32_MAX] sono display virtuali HWC e -1 rappresenta un display non valido o un display virtuale non HWC.

A partire da Android 10, ai display vengono assegnati ID stabili e persistenti, il che consente a SurfaceFlinger e DisplayManagerService di monitorare più di due display e riconoscere quelli già visti. Se l'HWC supporta IComposerClient.getDisplayIdentificationData e fornisce dati di identificazione del display, SurfaceFlinger analizza la struttura EDID e alloca ID display stabili a 64 bit per i display fisici e virtuali HWC. Gli ID sono espressi utilizzando un tipo di opzione, in cui il valore nullo rappresenta un display non valido o un display virtuale non HWC. Senza il supporto HWC, SurfaceFlinger torna al comportamento precedente con al massimo due display fisici.

Focus per display

Per supportare diverse origini di input che hanno come target singoli display contemporaneamente, Android 10 può essere configurato per supportare più finestre attive, al massimo una per display. Questo è destinato solo a tipi speciali di dispositivi quando più utenti interagiscono con lo stesso dispositivo contemporaneamente e utilizzano metodi o dispositivi di input diversi, ad esempio Android Automotive.

È vivamente consigliato di non attivare questa funzionalità per i dispositivi normali, inclusi quelli multi-schermo o quelli utilizzati per esperienze <x0A>simili a quelle desktop. Ciò è dovuto principalmente a un problema di sicurezza che potrebbe indurre gli utenti a chiedersi quale finestra abbia lo stato attivo.

Immagina l'utente che inserisce informazioni sicure in un campo di input di testo, magari accedendo a un'app di servizi bancari o inserendo un testo che contiene informazioni sensibili. Un'app dannosa potrebbe creare un display virtuale fuori schermo con cui eseguire un'attività, anche con un campo di input di testo. Le attività legittime e malicious hanno il focus e mostrano entrambe un indicatore di input attivo (cursore lampeggiante).

Tuttavia, poiché l'input da una tastiera (hardware o software) viene inserito solo nell'attività in primo piano (l'app avviata più di recente), creando un display virtuale nascosto, un'app dannosa potrebbe acquisire l'input dell'utente, anche quando si utilizza una tastiera software sul display del dispositivo principale.

Utilizza com.android.internal.R.bool.config_perDisplayFocusEnabled per impostare la messa a fuoco per display.

Compatibilità

Problema: in Android 9 e versioni precedenti, al massimo una finestra del sistema è attiva alla volta.

Soluzione: nel raro caso in cui due finestre dello stesso processo vengano messe a fuoco, il sistema mette a fuoco solo la finestra più in alto nell'ordine Z. Questa limitazione viene rimossa per le app che hanno come target Android 10, a quel punto è previsto che possano supportare più finestre su cui concentrarsi contemporaneamente.

Implementazione

WindowManagerService#mPerDisplayFocusEnabled controlla la disponibilità di questa funzionalità. In ActivityManager, ora viene utilizzato ActivityDisplay#getFocusedStack() anziché il monitoraggio globale in una variabile. ActivityDisplay#getFocusedStack() determina lo stato attivo in base all'ordine Z anziché memorizzare il valore nella cache. In questo modo, solo un'origine, WindowManager, deve tenere traccia dell'ordine Z delle attività.

ActivityStackSupervisor#getTopDisplayFocusedStack() adotta un approccio simile per i casi in cui deve essere identificato lo stack più in primo piano nel sistema. Gli stack vengono attraversati dall'alto verso il basso, alla ricerca del primo stack idoneo.

InputDispatcher ora può avere più finestre attive (una per display). Se un evento di input è specifico per il display, viene inviato alla finestra attiva nel display corrispondente. In caso contrario, viene inviato alla finestra attiva nel display attivo, ovvero il display con cui l'utente ha interagito più di recente.

Vedi InputDispatcher::mFocusedWindowHandlesByDisplay e InputDispatcher::setFocusedDisplay(). Anche le app mirate vengono aggiornate separatamente in InputManagerService tramite NativeInputManager::setFocusedApplication().

In WindowManager, anche le finestre attive vengono monitorate separatamente. Consulta DisplayContent#mCurrentFocus e DisplayContent#mFocusedApp e i rispettivi utilizzi. I metodi di monitoraggio e aggiornamento della messa a fuoco correlata sono stati spostati da WindowManagerService a DisplayContent.