Supporto display

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

Ridimensionare attività e display

Per indicare che un'app potrebbe non supportare la modalità a più finestre 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 immissione assolute (anziché quelle relative alla posizione della finestra), il che potrebbe interrompere l'immissione in modalità multi-finestra.

In Android 7 (e versioni successive), è possibile impostare un'appresizeableActivity=false in modo che venga eseguita sempre in modalità a schermo intero. In questo caso, la piattaforma impedisce alle attività non ridimensionabili di passare allo schermo diviso. Se l'utente tenta di richiamare un'attività non ridimensionabile dall'Avvio app 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 file manifest non devono essere avviate in modalità multi-finestra, a meno che non venga applicata la modalità di compatibilità:

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

In Android 10, la piattaforma impedisce comunque alle attività non ridimensionabili di passare alla modalità schermo diviso, ma possono essere scalate temporaneamente se l'attività ha dichiarato un orientamento o proporzioni fisse. In caso contrario, l'attività si ridimensiona per occupare l'intero schermo come in Android 9 e inferiori.

L'implementazione predefinita applica le seguenti norme:

Quando un'attività è dichiarata incompatibile con il multi-finestra tramite l'uso dell'attributo android:resizeableActivity e quando quell'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'opzione per riavviare il processo dell'app in modo da utilizzare la configurazione dello schermo aggiornata.

  • È con orientamento fisso tramite l'applicazione di android:screenOrientation
  • L'app ha proporzioni massime o minime predefinite in base al livello API di destinazione o dichiara le proporzioni esplicitamente

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

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

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

Implementazione

Un'attività non ridimensionabile con orientamento o proporzioni fisse è chiamata in codice modalità di compatibilità delle dimensioni (SCM). La condizione è definita in ActivityRecord#shouldUseSizeCompatMode(). Quando viene avviata un'attività SCM, la configurazione relativa allo schermo (come dimensioni o densità) viene fissata nella configurazione di override richiesta, quindi l'attività non dipende più dall'attuale configurazione del display.

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

Quando un'attività SCM utilizza una configurazione dello schermo diversa rispetto al suo contenitore (ad esempio, le dimensioni del display vengono modificate 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 nuove proporzioni, dalle proporzioni elevate di schermi lunghi e sottili alle proporzioni 1:1. Le app possono definire l'ApplicationInfo#maxAspectRatio e il ApplicationInfo#minAspectRatio dello schermo che possono gestire.

proporzioni app in Android 10

Figura 1. Rapporti di app di esempio supportati in Android 10

Le implementazioni dei dispositivi possono avere display secondari con dimensioni e risoluzioni inferiori a quelle richieste da Android 9 e versioni successive (minima di 6,3 cm di larghezza o altezza, minima di 320 DP per smallestScreenWidth), ma possono essere inserite solo le attività che attivano il supporto di questi display di piccole dimensioni.

Le app possono essere attivate dichiarando una dimensione minima supportata inferiore o uguale alla dimensione di visualizzazione di destinazione. A tale scopo, utilizza gli attributi di layout delle attività android:minHeight e android:minWidth in AndroidManifest.

Norme relative alla visualizzazione

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

  • Stato e rotazione del display
  • Alcuni tasti e rilevamento degli eventi di movimento
  • UI di sistema e finestre decorative

In Android 9 (e versioni precedenti), la classe PhoneWindowManager ha gestito i criteri di visualizzazione, lo stato e le impostazioni, la rotazione, il monitoraggio della cornice della finestra decorativa 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 gestione delle finestre configurabile per display è stata ampliata per includere:

  • Modalità di visualizzazione della finestra predefinita
  • Valori overscan
  • Modalità di rotazione e rotazione utenti
  • Dimensioni, densità e modalità di ridimensionamento forzati
  • Modalità di rimozione dei contenuti (quando la visualizzazione viene rimossa)
  • Supporto per le decorazioni di sistema e l'IME

La classe DisplayWindowSettings contiene le impostazioni per queste opzioni. Vengono mantenuti nel disco nella partizione /data in display_settings.xml ogni volta che viene modificata un'impostazione. Per maggiori dettagli, consulta 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 viene cancellato da un azzeramento.

Per impostazione predefinita, Android 10 utilizza DisplayInfo#uniqueId come identificatore per un display quando vengono mantenute le impostazioni. uniqueId deve essere compilato per tutte le visualizzazioni. 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 in modo da poter aggiornare in sicurezza la chiave utilizzata per una voce di visualizzazione nello spazio di archiviazione. Per maggiori dettagli, consulta Identificatori di visualizzazione statici.

Le impostazioni sono rese persistenti nella directory /data per motivi cronologici. In origine, venivano utilizzati per mantenere le impostazioni impostate dall'utente, come la rotazione dello schermo.

Identificatori di visualizzazione statici

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

Se un dispositivo dispone di più display disponibili dall'avvio, a questi display potrebbero essere assegnati identificatori diversi, a seconda dei tempi. Sebbene Android 9 (e versioni precedenti) includesse DisplayInfo#uniqueId, non conteneva informazioni sufficienti per distinguere i display perché i display fisici erano identificati come local:0 o local:1 per rappresentare il display integrato e quello esterno.

Android 10 cambia DisplayInfo#uniqueId per aggiungere un identificatore stabile e per distinguere 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 di visualizzazione stabile dopo i riavvii. In Android 10, DisplayAddress supporta display fisici e di rete. DisplayAddress.Physical contiene un ID visualizzato stabile (come in uniqueId) e può essere creato con DisplayAddress#fromPhysicalDisplayId().

Android 10 offre anche un metodo pratico per ottenere informazioni sulle porte (Physical#getPort()), che può essere utilizzato nel framework per identificare i display in modo statico. 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 con più display e di configurare diverse impostazioni e funzionalità di sistema utilizzando identificatori di display statici, ad esempio le porte per i display fisici. Questi metodi sono nascosti e possono essere utilizzati 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'uscita del display, nonché il blob EDID del display. SurfaceFlinger estrae le informazioni sul produttore o sul modello dall'EDID per generare gli ID di visualizzazione stabili a 64 bit esposti al framework. Se questo metodo non è supportato o genera errori, SurfaceFlinger passa alla modalità MD precedente, dove DisplayInfo#address è nullo e DisplayInfo#uniqueId è hardcoded, come descritto sopra.

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 massimo due display fisici con ID hardcoded 0 e 1.

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

Il framework può cercare il token IBinder per trovare una visualizzazione fisica tramite SurfaceControl#getPhysicalDisplayToken dopo aver recuperato 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, gli altri display interni vengono considerati esterni. Come soluzione alternativa, il codice specifico del dispositivo può fare supposizioni su DisplayAddress.Physical#getPort se l'HWC è noto e la logica di allocazione delle porte è prevedibile.

Questa limitazione è stata 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) non è pertinente. Tuttavia, rimane vero che il display principale non può essere scollegato e ne consegue che deve essere un display interno. Alcuni smartphone pieghevoli hanno più display interni.
  • I display secondari sono classificati correttamente come Display.TYPE_INTERNAL o Display.TYPE_EXTERNAL (precedentemente noti rispettivamente come Display.TYPE_BUILT_IN e Display.TYPE_HDMI) a seconda del tipo di connessione.

Implementazione

In Android 9 e versioni precedenti, i display sono 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 non HWC.

A partire da Android 10, ai display vengono assegnati ID stabili e permanenti, che consentono a SurfaceFlinger e DisplayManagerService di monitorare più di due display e di riconoscere quelli visti in precedenza. Se HWC supporta IComposerClient.getDisplayIdentificationData e fornisce dati di identificazione dei display, SurfaceFlinger analizza la struttura EDID e alloca ID di visualizzazione stabili a 64 bit per i display fisici e virtuali HWC. Gli ID vengono 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 di HWC, SurfaceFlinger utilizza il comportamento precedente con al massimo due display fisici.

Focus per display

Per supportare più origini di input che hanno come target singoli display contemporaneamente, Android 10 può essere configurato per supportare più finestre in primo piano, al massimo una per display. Questa opzione è pensata solo per tipi speciali di dispositivi quando più utenti interagiscono con lo stesso dispositivo contemporaneamente e utilizzano dispositivi o metodi di inserimento diversi, ad esempio Android Automotive.

È vivamente sconsigliato attivare questa funzionalità per i dispositivi normali, inclusi i dispositivi multischermo o quelli utilizzati per esperienze simili a quelle dei computer. Ciò è dovuto principalmente a un problema di sicurezza che potrebbe indurre gli utenti a chiedersi quale finestra ha lo stato attivo dell'input.

Immagina un utente che inserisce informazioni sicure in un campo di immissione di testo, ad esempio accedendo a un'app di online banking o inserendo del testo contenente informazioni sensibili. Un'app dannosa potrebbe creare un display virtuale off-screen con cui eseguire un'attività, anche con un campo di immissione di testo. Le attività legittime e dannose sono attive ed entrambe mostrano un indicatore di input attivo (cursore lampeggiante).

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

Usa com.android.internal.R.bool.config_perDisplayFocusEnabled per impostare lo stato attivo per display.

Compatibilità

Problema: in Android 9 e versioni precedenti, al massimo una finestra del sistema ha il fuoco alla volta.

Soluzione: nel raro caso in cui due finestre dello stesso processo siano messe a fuoco, il sistema mette a fuoco solo la finestra più alta nell'ordine Z. Questa limitazione viene rimossa per le app destinate ad Android 10, quindi è previsto che possano supportare più finestre contemporaneamente.

Implementazione

WindowManagerService#mPerDisplayFocusEnabled controlla la disponibilità di questa funzionalità. In ActivityManager, ora viene utilizzato ActivityDisplay#getFocusedStack() al posto del monitoraggio globale in una variabile. ActivityDisplay#getFocusedStack() determina lo stato attivo in base all'ordine Z anziché memorizzare nella cache il valore. In questo modo, soltanto una sorgente, WindowManager, deve monitorare l'ordine Z delle attività.

ActivityStackSupervisor#getTopDisplayFocusedStack() adotta un approccio simile per i casi in cui è necessario identificare la pila più in primo piano nel sistema. Le pila vengono attraversate dall'alto verso il basso, alla ricerca della prima pila idonea.

Ora InputDispatcher può avere più finestre con stato attivo (una per display). Se un evento di input è specifico per la visualizzazione, viene inviato alla finestra attiva nella visualizzazione corrispondente. Altrimenti, viene inviato alla finestra con lo stato attivo, che è il display con cui l'utente ha interagito più di recente.

Leggi InputDispatcher::mFocusedWindowHandlesByDisplay e InputDispatcher::setFocusedDisplay(). Anche le app in primo piano 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 correlati ai focus sono stati spostati da WindowManagerService a DisplayContent.