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à multifinestra 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 dal programma di avvio mentre è già in modalità schermo diviso, la piattaforma esce da questa modalità 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à 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 ancora alle attività non ridimensionabili di passare alla modalità schermo diviso, 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 la seguente norma:

Quando un'attività è dichiarata incompatibile con il multi-finestra tramite l'utilizzo 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.

  • L'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 un'area visibile dichiarata. Quando il dispositivo viene piegato, la finestra viene ridotta in base all'area mantenendo le proporzioni utilizzando il letterbox appropriato. Inoltre, ogni volta che l'area di visualizzazione dell'attività viene modificata, all'utente viene fornita un'opzione per riavviare l'attività.

Quando apri 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 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 (ad esempio le dimensioni o la densità) viene fissata nella configurazione di override richiesta, pertanto 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 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 degli schermi lunghi e sottili alle proporzioni 1:1. Le app possono definire ApplicationInfo#maxAspectRatio e il ApplicationInfo#minAspectRatio dello schermo che sono in grado di gestire.

Rapporti di 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 solo le attività che attivano il supporto di questi piccoli display possono essere posizionate lì.

Le app possono attivare questa funzionalità dichiarando una dimensione minima supportata inferiore o uguale alle dimensioni del display target. A tale scopo, utilizza gli attributi di layout dell'attività android:minHeight e android:minWidth in AndroidManifest.

Norme relative alla visualizzazione

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

  • Stato e rotazione del display
  • Alcune chiavi e il monitoraggio degli eventi di movimento
  • UI di sistema e finestre di decorazione

In Android 9 (e versioni precedenti), la classe PhoneWindowManager gestiva i criteri di visualizzazione, lo stato e le impostazioni, la rotazione, il monitoraggio del riquadro della finestra di decorazione e altro ancora. In Android 10 la maggior parte di questi dati viene trasferita alla 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 di overscan
  • Rotazione dell'utente e modalità di rotazione
  • 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 memorizzati sul 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 utilizzaDisplayInfo#uniqueId come identificatore di una visualizzazione quando mantiene le impostazioni. uniqueId deve essere compilato per tutti i display. Inoltre, è stabile per i display fisici e di rete. È anche possibile usare 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 vengono mantenute nella directory /data per motivi storici. In origine, venivano utilizzati per mantenere le impostazioni impostate dall'utente, ad esempio la rotazione del display.

Identificatori di visualizzazione statici

Android 9 (e versioni precedenti) non forniva identificatori stabili per le visualizzazioni nel framework. Quando un display è stato aggiunto 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 ha 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 modifica DisplayInfo#uniqueId per aggiungere un identificatore stabile e per 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 di visualizzazione stabile dopo i riavvii. In Android 10, DisplayAddress supporta i display fisici e di rete. DisplayAddress.Physical contiene un ID visualizzato stabile (come in uniqueId) e può essere creato con DisplayAddress#fromPhysicalDisplayId().

Android 10 fornisce anche un comodo metodo per ottenere informazioni sulla porta (Physical#getPort()). Questo metodo può essere utilizzato nel framework per identificare in modo statico le visualizzazioni. 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 sono destinati solo all'uso in system_server.

IComposerClient

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 display a 64 bit stabili 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 potrebbe sfruttare 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 di visualizzazione 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 DisplayEventReceiver hotplug.

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 è rilevante. Tuttavia, rimane vero che il display principale non può essere scollegato e ne consegue che deve essere un display interno. Tieni presente che alcuni smartphone pieghevoli hanno più display interni.
  • Le visualizzazioni secondarie sono classificate correttamente come Display.TYPE_INTERNAL o Display.TYPE_EXTERNAL (precedentemente Display.TYPE_BUILT_IN e Display.TYPE_HDMI, rispettivamente) 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 un display virtuale 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 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 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 HWC, SurfaceFlinger torna al comportamento precedente con un massimo di due display fisici.

Messa a fuoco 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 input 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 portare gli utenti a chiedersi su quale finestra è attivo l'input.

Immagina un utente che inserisce informazioni protette 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 off-screen con cui eseguire un'attività, anche con un campo di immissione di testo. Le attività legittime e dannose sono attive e mostrano entrambe un indicatore di input attivo (cursore lampeggiante).

Tuttavia, poiché l'input di una tastiera (hardware o software) viene inserito solo nell'attività più in alto (l'app lanciata 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.

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, che dovrebbero supportare il passaggio di più finestre in primo piano contemporaneamente.

Implementazione

WindowManagerService#mPerDisplayFocusEnabled controlla la disponibilità di questa funzionalità. In ActivityManager, ActivityDisplay#getFocusedStack() viene ora utilizzato 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 serie vengono attraversate dall'alto verso il basso, cercando la prima serie idonea.

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 inviata alla finestra attiva nel display attivo, ovvero 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.