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 del display dal contesto dell'app. I valori restituiti non verranno regolati 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 le coordinate di input assolute (anziché quelle relative alla posizione della finestra), il che potrebbe interrompere l'input in multi-finestra.

In Android 7 (e versioni successive), un'app può essere impostata su 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 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 di 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à schermo diviso, ma possono essere ridimensionate temporaneamente se l'attività ha dichiarato un orientamento o una proporzione fissa. In caso contrario, l'attività viene ridimensionata per riempire l'intero schermo come in Android 9 e versioni precedenti.

L'implementazione predefinita applica la seguente policy:

Quando un'attività dichiarata incompatibile con la modalità multi-finestra tramite l'utilizzo dell'attributo android:resizeableActivity e quando l'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 una funzionalità per riavviare il processo dell'app per utilizzare la configurazione dello schermo aggiornata.

  • Orientamento fisso tramite l'applicazione di android:screenOrientation
  • L'app ha una proporzione massima o minima predefinita come target del livello API o dichiara esplicitamente la proporzione

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

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

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

Implementazione

Un'attività non ridimensionabile con orientamento o proporzione fissa viene chiamata 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 (ad esempio dimensioni o densità) viene corretta nella configurazione di override richiesta, quindi l'attività non dipende più dalla configurazione del display 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 suo contenitore (ad esempio, il display viene ridimensionato o l'attività viene spostata su un altro display), ActivityRecord#inSizeCompatMode() è true e SizeCompatModeActivityController (nell'UI 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 ApplicationInfo#maxAspectRatio e ApplicationInfo#minAspectRatio dello schermo che sono in grado di gestire.

Proporzioni delle app in Android 10

Figura 1. Proporzioni delle app di esempio 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 accettare dichiarando una dimensione minima supportata inferiore o uguale alla dimensione del display di destinazione. A questo scopo, utilizza gli attributi di layout dell'attività android:minHeight e android:minWidth in AndroidManifest.

Policy del display

Android 10 separa e sposta alcune policy del display dall'implementazione WindowManagerPolicy predefinita in PhoneWindowManager alle classi per display, ad esempio:

  • Stato e rotazione del display
  • Monitoraggio di alcuni tasti ed eventi di movimento
  • UI di sistema e finestre di decorazione

In Android 9 (e versioni precedenti), la classe PhoneWindowManager gestiva le policy, lo stato e le impostazioni del display, la rotazione, il monitoraggio dei frame delle finestre 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 del display

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

  • Modalità di windowing del display predefinita
  • Valori di overscan
  • Rotazione e modalità di rotazione dell'utente
  • Dimensioni, densità e modalità di ridimensionamento forzate
  • Modalità di rimozione dei contenuti (quando il display viene rimosso)
  • Supporto per decorazioni di sistema e IME

La classe DisplayWindowSettings contiene le impostazioni per queste opzioni. Vengono salvate 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 ripristinare il file se viene cancellato da una cancellazione.

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 del display nello spazio di archiviazione. Per i dettagli, vedi Identificatori di display statici.

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

Identificatori di display statici

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

Se un dispositivo aveva più display disponibili all'avvio, ai display potevano essere assegnati identificatori diversi, a seconda della tempistica. Sebbene Android 9 (e versioni precedenti) includesse DisplayInfo#uniqueId, non conteneva informazioni sufficienti per distinguere tra i display perché i display fisici venivano 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 i 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 multi-display statiche 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 sul produttore o sul modello dall'EDID per generare gli ID display stabili a 64 bit esposti al framework. Se questo metodo non è supportato o genera un errore, SurfaceFlinger torna alla modalità MD legacy, in cui DisplayInfo#address è null 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 al massimo due display fisici con ID hardcoded 0 e 1.

A partire da Android 10, SurfaceFlinger potrebbe sfruttare un'API Hardware Composer (HWC) per generare ID display stabili, che gli consente di gestire un numero arbitrario di display fisici. Per scoprire di più, vedi Identificatori di 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) non è pertinente. Tuttavia, rimane vero che il display principale non può essere disconnesso e, di conseguenza, in pratica deve essere un display interno. Tieni presente che alcuni smartphone pieghevoli hanno più display interni.
  • I display secondari vengono classificati correttamente come Display.TYPE_INTERNAL o Display.TYPE_EXTERNAL (in precedenza 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 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, che consentono a SurfaceFlinger e DisplayManagerService di monitorare più di due display e riconoscere i display visualizzati in precedenza. Se l'HWC supporta IComposerClient.getDisplayIdentificationData e fornisce i 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 vengono espressi utilizzando un tipo di opzione, in cui il valore null rappresenta un display non valido o un display virtuale non HWC. Senza il supporto HWC, SurfaceFlinger torna al comportamento legacy 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 con focus, al massimo una per display. Questa funzionalità è destinata 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 consigliabile non attivare questa funzionalità per i dispositivi normali, inclusi i dispositivi multi-schermo o quelli utilizzati per esperienze simili a quelle desktop. Ciò è dovuto principalmente a un problema di sicurezza che potrebbe indurre gli utenti a chiedersi quale finestra abbia il focus di input.

Immagina l'utente che inserisce informazioni sicure in un campo di immissione di testo, magari accedendo a un'app bancaria o inserendo testo contenente informazioni sensibili. Un'app dannosa potrebbe creare un display virtuale fuori schermo con cui eseguire un'attività, anche con un campo di immissione di testo. Le attività legittime e dannose hanno il focus 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à più in alto (l'app avviata più di recente), creando un display virtuale nascosto, un'app dannosa potrebbe acquisire l'input utente, anche quando si utilizza una tastiera su schermo sul display del dispositivo principale.

Utilizza com.android.internal.R.bool.config_perDisplayFocusEnabled per impostare il focus per display.

Compatibilità

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

Soluzione: nel raro caso in cui due finestre dello stesso processo abbiano il focus, il sistema fornisce il focus solo alla finestra più in alto nell'ordine Z. Questa limitazione viene rimossa per le app che hanno come target Android 10, a quel punto si prevede che possano supportare più finestre con focus 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 il focus in base all'ordine Z anziché memorizzare nella cache il valore. In questo modo, solo un'origine, WindowManager, deve monitorare l'ordine Z delle attività.

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

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

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

In WindowManager, anche le finestre con focus vengono monitorate separatamente. Vedi DisplayContent#mCurrentFocus e DisplayContent#mFocusedApp e i rispettivi utilizzi. I metodi di monitoraggio e aggiornamento del focus correlati sono stati spostati da WindowManagerService a DisplayContent.