Поддержка дисплея

Обновления, внесенные в эти области дисплея, представлены ниже:

Изменение размера действий и отображений

Чтобы указать, что приложение может не поддерживать многооконный режим или изменение размера, действия используют атрибут resizeableActivity=false . Общие проблемы, с которыми сталкиваются приложения при изменении размера действий, включают:

  • Конфигурация действия может отличаться от конфигурации приложения или другого невизуального компонента. Распространенной ошибкой является чтение показателей отображения из контекста приложения. Возвращаемые значения не будут корректироваться в соответствии с метриками видимой области, в которой отображается действие.
  • Действие может не обрабатывать изменение размера и аварийно завершать работу, отображать искаженный пользовательский интерфейс или терять состояние из-за перезапуска без сохранения состояния экземпляра.
  • Приложение может попытаться использовать абсолютные координаты ввода (вместо координат, относящихся к положению окна), что может привести к нарушению ввода в многооконном режиме.

В Android 7 (и более поздних версиях) для приложения можно установить resizeableActivity=false , чтобы оно всегда запускалось в полноэкранном режиме. В этом случае платформа предотвращает переход действий без изменения размера на разделенный экран. Если пользователь пытается вызвать действие без изменения размера из средства запуска, находясь уже в режиме разделенного экрана, платформа выходит из режима разделенного экрана и запускает действие без изменения размера в полноэкранном режиме.

Приложения, которые явно устанавливают для этого атрибута значение false в манифесте, не должны запускаться в многооконном режиме, если не применяется режим совместимости:

  • Та же конфигурация применяется к процессу, который содержит все компоненты активности и неактивности.
  • Применяемая конфигурация соответствует требованиям CDD для дисплеев, совместимых с приложениями.

В Android 10 платформа по-прежнему предотвращает переход действий без изменения размера в режим разделенного экрана, но их можно временно масштабировать, если для действия объявлена ​​фиксированная ориентация или соотношение сторон. В противном случае размер действия изменяется, чтобы заполнить весь экран, как в Android 9 и более ранних версиях.

Реализация по умолчанию применяет следующую политику:

Когда действие объявлено несовместимым с многооконностью посредством использования атрибута android:resizeableActivity и когда это действие соответствует одному из условий, описанных ниже, тогда, когда применяемая конфигурация экрана должна измениться, действие и процесс сохраняются с исходной конфигурацией, и пользователю предоставляется возможность перезапустить процесс приложения для использования обновленной конфигурации экрана.

  • Фиксированная ориентация через приложение android:screenOrientation
  • Приложение имеет максимальное или минимальное соотношение сторон по умолчанию, ориентируясь на уровень API, или явно объявляет соотношение сторон.

На этом рисунке показано действие без изменения размера с заявленным соотношением сторон. При складывании устройства окно уменьшается по размеру, сохраняя при этом соотношение сторон с использованием соответствующего почтового ящика. Кроме того, пользователю предоставляется возможность перезапуска действия каждый раз, когда изменяется область отображения действия.

При разворачивании устройства конфигурация, размер и соотношение сторон активности не изменяются, но отображается возможность перезапустить активность.

Если resizeableActivity не установлен (или для него установлено значение true ), приложение полностью поддерживает изменение размера.

Выполнение

Действие без изменения размера с фиксированной ориентацией или соотношением сторон в коде называется режимом совместимости размеров (SCM). Условие определяется в ActivityRecord#shouldUseSizeCompatMode() . При запуске действия SCM конфигурация, связанная с экраном (например, размер или плотность), фиксируется в запрошенной конфигурации переопределения, поэтому действие больше не зависит от текущей конфигурации дисплея.

Если действие SCM не может заполнить весь экран, оно выравнивается по верху и центрируется по горизонтали. Границы активности вычисляются с помощью AppWindowToken#calculateCompatBoundsTransformation() .

Когда действие SCM использует конфигурацию экрана, отличную от конфигурации его контейнера (например, изменяется размер дисплея или действие перемещается на другой дисплей), ActivityRecord#inSizeCompatMode() имеет значение true, а SizeCompatModeActivityController (в системном пользовательском интерфейсе) получает обратный вызов для отображения кнопки перезапуска процесса.

Размеры дисплея и соотношение сторон

Android 10 обеспечивает поддержку новых соотношений сторон: от высоких соотношений длинных и тонких экранов до соотношений 1:1. Приложения могут определять ApplicationInfo#maxAspectRatio и ApplicationInfo#minAspectRatio экрана, с которым они могут работать.

соотношение приложений в Android 10

Рисунок 1. Пример соотношения приложений, поддерживаемых в Android 10.

Реализации устройств могут иметь дополнительные дисплеи с размерами и разрешением меньше, чем те, которые требуются для Android 9, и ниже (минимум 2,5 дюйма в ширину или высоту, минимум 320 DP для smallestScreenWidth ), но там могут быть размещены только те действия, которые соглашаются поддерживать эти маленькие дисплеи.

Приложения могут согласиться, указав минимальный поддерживаемый размер, который меньше или равен целевому размеру дисплея. Для этого используйте атрибуты макета действий android:minHeight и android:minWidth в AndroidManifest.

Политики отображения

Android 10 отделяет и перемещает определенные политики отображения из реализации WindowManagerPolicy по умолчанию в PhoneWindowManager в классы для каждого дисплея, например:

  • Состояние отображения и поворот
  • Некоторые клавиши и отслеживание событий движения
  • Системный интерфейс и декоративные окна

В Android 9 (и более ранних версиях) класс PhoneWindowManager обрабатывал политики отображения, состояние и настройки, вращение, отслеживание рамки декоративного окна и многое другое. В Android 10 большая часть этого перенесена в класс DisplayPolicy , за исключением отслеживания вращения, которое было перенесено в DisplayRotation .

Настройки окна дисплея

В Android 10 настраиваемые параметры окон для каждого дисплея были расширены и теперь включают:

  • Оконный режим отображения по умолчанию
  • Значения пересканирования
  • Ротация пользователя и режим ротации
  • Принудительный размер, плотность и режим масштабирования
  • Режим удаления контента (когда дисплей удален)
  • Поддержка системных украшений и IME

Класс DisplayWindowSettings содержит настройки для этих параметров. Они сохраняются на диске в разделе /data в display_settings.xml каждый раз при изменении параметра. Дополнительные сведения см. в разделах DisplayWindowSettings.AtomicFileStorage и DisplayWindowSettings#writeSettings() . Производители устройств могут указать значения по умолчанию в display_settings.xml для конфигурации своих устройств. Однако, поскольку файл хранится в /data , для восстановления файла в случае его удаления может потребоваться дополнительная логика.

По умолчанию Android 10 использует DisplayInfo#uniqueId в качестве идентификатора дисплея при сохранении настроек. uniqueId должен быть заполнен для всех дисплеев. Кроме того, он стабилен для физических и сетевых дисплеев. Также можно использовать порт физического дисплея в качестве идентификатора, который можно установить в DisplayWindowSettings#mIdentifier . При каждой записи записываются все настройки, поэтому можно безопасно обновить ключ, используемый для отображаемой записи в хранилище. Подробности см. в разделе Статические идентификаторы отображения .

Настройки сохраняются в каталоге /data по историческим причинам. Первоначально они использовались для сохранения установленных пользователем настроек, таких как поворот дисплея.

Статические идентификаторы отображения

Android 9 (и более ранние версии) не обеспечивал стабильных идентификаторов дисплеев в рамках. При добавлении дисплея в систему для этого дисплея создавались Display#mDisplayId или DisplayInfo#displayId путем увеличения статического счетчика. Если система добавляла и удаляла один и тот же дисплей, получался другой идентификатор.

Если на устройстве было несколько дисплеев, доступных при загрузке, дисплеям могли быть назначены разные идентификаторы в зависимости от времени. Хотя Android 9 (и более ранние версии) включал DisplayInfo#uniqueId , он не содержал достаточно информации для различения дисплеев, поскольку физические дисплеи были идентифицированы как local:0 или local:1 , чтобы представлять встроенный и внешний дисплей.

В Android 10 изменен DisplayInfo#uniqueId , чтобы добавить стабильный идентификатор и различать локальные, сетевые и виртуальные дисплеи.

Тип дисплея Формат
Местный
local:<stable-id>
Сеть
network:<mac-address>
Виртуальный
virtual:<package-name-and-name>

В дополнение к обновлениям uniqueId DisplayInfo.address содержит DisplayAddress — идентификатор отображения, который остается стабильным при перезагрузках. В Android 10 DisplayAddress поддерживает физические и сетевые дисплеи. DisplayAddress.Physical содержит стабильный идентификатор дисплея (тот же, что и uniqueId ) и может быть создан с помощью DisplayAddress#fromPhysicalDisplayId() .

Android 10 также предоставляет удобный метод получения информации о порте ( Physical#getPort() ). Этот метод можно использовать в рамках статической идентификации дисплеев. Например, он используется в DisplayWindowSettings ). DisplayAddress.Network содержит MAC-адрес и может быть создан с помощью DisplayAddress#fromMacAddress() .

Эти дополнения позволяют производителям устройств идентифицировать дисплеи в статических конфигурациях с несколькими дисплеями и настраивать различные параметры и функции системы, используя статические идентификаторы дисплеев, такие как порты для физических дисплеев. Эти методы скрыты и предназначены только для использования внутри system_server .

Учитывая идентификатор дисплея HWC (который может быть непрозрачным и не всегда стабильным), этот метод возвращает (зависит от платформы) 8-битный номер порта, который идентифицирует физический разъем для вывода дисплея, а также большой двоичный объект EDID дисплея. SurfaceFlinger извлекает информацию о производителе или модели из EDID для создания стабильных 64-битных идентификаторов дисплея, доступных для платформы. Если этот метод не поддерживается или возникает ошибка, SurfaceFlinger возвращается к устаревшему режиму MD, где DisplayInfo#address имеет значение null, а DisplayInfo#uniqueId жестко запрограммировано, как описано выше.

Чтобы убедиться, что эта функция поддерживается, запустите:

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

Используйте более двух дисплеев

В Android 9 (и более ранних версиях) SurfaceFlinger и DisplayManagerService предполагали наличие не более двух физических дисплеев с жестко закодированными идентификаторами 0 и 1.

Начиная с Android 10, SurfaceFlinger может использовать API Hardware Composer (HWC) для генерации стабильных идентификаторов дисплеев, что позволяет ему управлять произвольным количеством физических дисплеев. Дополнительные сведения см. в разделе Статические идентификаторы отображения .

Платформа может искать токен IBinder для физического дисплея через SurfaceControl#getPhysicalDisplayToken после получения 64-битного идентификатора дисплея из SurfaceControl#getPhysicalDisplayIds или из события горячего подключения DisplayEventReceiver .

В Android 10 (и более ранних версиях) основным внутренним дисплеем является TYPE_INTERNAL , а все дополнительные дисплеи помечаются как TYPE_EXTERNAL независимо от типа подключения. Поэтому дополнительные внутренние дисплеи считаются внешними. В качестве обходного пути код конкретного устройства может делать предположения относительно DisplayAddress.Physical#getPort если HWC известен и логика выделения портов предсказуема.

Это ограничение снято в Android 11 (и более поздних версиях).

  • В Android 11 первый дисплей, отображаемый во время загрузки, является основным дисплеем. Тип подключения (внутреннее или внешнее) не имеет значения. Однако остается верным, что основной дисплей не может быть отключен, и из этого следует, что на практике он должен быть внутренним дисплеем. Обратите внимание, что некоторые складные телефоны имеют несколько внутренних дисплеев.
  • Вторичные дисплеи правильно классифицируются как Display.TYPE_INTERNAL или Display.TYPE_EXTERNAL (ранее известные как Display.TYPE_BUILT_IN и Display.TYPE_HDMI соответственно) в зависимости от типа их подключения.

Выполнение

В Android 9 и более ранних версиях дисплеи идентифицируются по 32-битным идентификаторам, где 0 — внутренний дисплей, 1 — внешний дисплей, [2, INT32_MAX] — виртуальные дисплеи HWC, а -1 представляет собой недопустимый дисплей или виртуальный дисплей, отличный от HWC.

Начиная с Android 10, дисплеям присваиваются стабильные и постоянные идентификаторы, что позволяет SurfaceFlinger и DisplayManagerService отслеживать более двух дисплеев и распознавать ранее просмотренные дисплеи. Если HWC поддерживает IComposerClient.getDisplayIdentificationData и предоставляет данные идентификации дисплея, SurfaceFlinger анализирует структуру EDID и выделяет стабильные 64-битные идентификаторы дисплея для физических и виртуальных дисплеев HWC. Идентификаторы выражаются с использованием типа параметра, где нулевое значение представляет недопустимый дисплей или виртуальный дисплей, отличный от HWC. Без поддержки HWC SurfaceFlinger возвращается к устаревшему поведению с максимум двумя физическими дисплеями.

Фокус на каждом дисплее

Для поддержки нескольких источников входного сигнала, ориентированных на отдельные дисплеи одновременно, Android 10 можно настроить на поддержку нескольких сфокусированных окон, не более одного на каждый дисплей. Это предназначено только для особых типов устройств, когда несколько пользователей одновременно взаимодействуют с одним и тем же устройством и используют разные методы или устройства ввода, например Android Automotive.

Настоятельно рекомендуется не включать эту функцию для обычных устройств, включая многоэкранные устройства или устройства, используемые для работы наподобие настольных компьютеров. Это связано прежде всего с проблемами безопасности, которые могут заставить пользователей задаться вопросом, какое окно имеет фокус ввода.

Представьте себе пользователя, который вводит защищенную информацию в поле ввода текста, например, входит в банковское приложение или вводит текст, содержащий конфиденциальную информацию. Вредоносное приложение может создать виртуальный закадровый дисплей для выполнения действий, также с полем для ввода текста. Законные и вредоносные действия имеют фокус, и оба отображают активный индикатор ввода (мигающий курсор).

Однако, поскольку ввод с клавиатуры (аппаратной или программной) вводится только в самое верхнее действие (то приложение, которое было запущено последним), путем создания скрытого виртуального дисплея вредоносное приложение может перехватить ввод пользователя, даже при использовании программной клавиатуры на основном дисплее устройства.

Используйте com.android.internal.R.bool.config_perDisplayFocusEnabled , чтобы установить фокус для каждого дисплея.

Совместимость

Проблема. В Android 9 и более ранних версиях фокус одновременно находится не более чем на одном окне системы.

Решение: В том редком случае, когда два окна одного и того же процесса будут сфокусированы, система фокусирует только то окно, которое находится выше в Z-порядке. Это ограничение снято для приложений, ориентированных на Android 10, после чего ожидается, что они смогут поддерживать одновременное фокусирование на нескольких окнах.

Выполнение

WindowManagerService#mPerDisplayFocusEnabled управляет доступностью этой функции. В ActivityManager вместо глобального отслеживания в переменной теперь используется ActivityDisplay#getFocusedStack() . ActivityDisplay#getFocusedStack() определяет фокус на основе Z-порядка вместо кэширования значения. Это значит, что только один источник, WindowManager, должен отслеживать Z-порядок действий.

ActivityStackSupervisor#getTopDisplayFocusedStack() использует аналогичный подход для тех случаев, когда необходимо определить самый верхний сфокусированный стек в системе. Стеки просматриваются сверху вниз в поисках первой подходящей стопки.

InputDispatcher теперь может иметь несколько окон в фокусе (по одному на каждый дисплей). Если входное событие зависит от дисплея, оно отправляется в сфокусированное окно на соответствующем дисплее. В противном случае оно отправляется в сфокусированное окно на сфокусированном дисплее, то есть на дисплее, с которым пользователь взаимодействовал в последний раз.

См. InputDispatcher::mFocusedWindowHandlesByDisplay и InputDispatcher::setFocusedDisplay() . Сосредоточенные приложения также обновляются отдельно в InputManagerService через NativeInputManager::setFocusedApplication() .

В WindowManager сфокусированные окна также отслеживаются отдельно. См. DisplayContent#mCurrentFocus и DisplayContent#mFocusedApp и соответствующие варианты использования. Соответствующие методы отслеживания и обновления фокуса были перенесены из WindowManagerService в DisplayContent .

,

Обновления, внесенные в эти области дисплея, представлены ниже:

Изменение размера действий и отображений

Чтобы указать, что приложение может не поддерживать многооконный режим или изменение размера, действия используют атрибут resizeableActivity=false . Общие проблемы, с которыми сталкиваются приложения при изменении размера действий, включают:

  • Конфигурация действия может отличаться от конфигурации приложения или другого невизуального компонента. Распространенной ошибкой является чтение показателей отображения из контекста приложения. Возвращаемые значения не будут корректироваться в соответствии с метриками видимой области, в которой отображается действие.
  • Действие может не обрабатывать изменение размера и аварийно завершать работу, отображать искаженный пользовательский интерфейс или терять состояние из-за перезапуска без сохранения состояния экземпляра.
  • Приложение может попытаться использовать абсолютные координаты ввода (вместо координат, относящихся к положению окна), что может привести к нарушению ввода в многооконном режиме.

В Android 7 (и более поздних версиях) для приложения можно установить resizeableActivity=false , чтобы оно всегда запускалось в полноэкранном режиме. В этом случае платформа предотвращает переход действий без изменения размера на разделенный экран. Если пользователь пытается вызвать действие без изменения размера из средства запуска, находясь уже в режиме разделенного экрана, платформа выходит из режима разделенного экрана и запускает действие без изменения размера в полноэкранном режиме.

Приложения, которые явно устанавливают для этого атрибута значение false в манифесте, не должны запускаться в многооконном режиме, если не применяется режим совместимости:

  • Та же конфигурация применяется к процессу, который содержит все компоненты активности и неактивности.
  • Применяемая конфигурация соответствует требованиям CDD для дисплеев, совместимых с приложениями.

В Android 10 платформа по-прежнему предотвращает переход действий без изменения размера в режим разделенного экрана, но их можно временно масштабировать, если для действия объявлена ​​фиксированная ориентация или соотношение сторон. В противном случае размер действия изменяется, чтобы заполнить весь экран, как в Android 9 и более ранних версиях.

Реализация по умолчанию применяет следующую политику:

Когда действие объявлено несовместимым с многооконностью посредством использования атрибута android:resizeableActivity и когда это действие соответствует одному из условий, описанных ниже, тогда, когда применяемая конфигурация экрана должна измениться, действие и процесс сохраняются с исходной конфигурацией, и пользователю предоставляется возможность перезапустить процесс приложения для использования обновленной конфигурации экрана.

  • Фиксированная ориентация через приложение android:screenOrientation
  • Приложение имеет максимальное или минимальное соотношение сторон по умолчанию, ориентируясь на уровень API, или явно объявляет соотношение сторон.

На этом рисунке показано действие без изменения размера с заявленным соотношением сторон. При складывании устройства окно уменьшается по размеру, сохраняя при этом соотношение сторон с использованием соответствующего почтового ящика. Кроме того, пользователю предоставляется возможность перезапуска действия каждый раз, когда изменяется область отображения действия.

При разворачивании устройства конфигурация, размер и соотношение сторон активности не изменяются, но отображается возможность перезапустить активность.

Если resizeableActivity не установлен (или для него установлено значение true ), приложение полностью поддерживает изменение размера.

Выполнение

Действие без изменения размера с фиксированной ориентацией или соотношением сторон в коде называется режимом совместимости размеров (SCM). Условие определяется в ActivityRecord#shouldUseSizeCompatMode() . При запуске действия SCM конфигурация, связанная с экраном (например, размер или плотность), фиксируется в запрошенной конфигурации переопределения, поэтому действие больше не зависит от текущей конфигурации дисплея.

Если действие SCM не может заполнить весь экран, оно выравнивается по верху и центрируется по горизонтали. Границы активности вычисляются с помощью AppWindowToken#calculateCompatBoundsTransformation() .

Когда действие SCM использует конфигурацию экрана, отличную от конфигурации его контейнера (например, изменяется размер дисплея или действие перемещается на другой дисплей), ActivityRecord#inSizeCompatMode() имеет значение true, а SizeCompatModeActivityController (в системном пользовательском интерфейсе) получает обратный вызов для отображения кнопки перезапуска процесса.

Размеры дисплея и соотношение сторон

Android 10 обеспечивает поддержку новых соотношений сторон: от высоких соотношений длинных и тонких экранов до соотношений 1:1. Приложения могут определять ApplicationInfo#maxAspectRatio и ApplicationInfo#minAspectRatio экрана, с которым они могут работать.

соотношение приложений в Android 10

Рисунок 1. Пример соотношения приложений, поддерживаемых в Android 10.

Реализации устройств могут иметь дополнительные дисплеи с размерами и разрешением меньше, чем те, которые требуются для Android 9, и ниже (минимум 2,5 дюйма в ширину или высоту, минимум 320 DP для smallestScreenWidth ), но там могут быть размещены только те действия, которые соглашаются поддерживать эти маленькие дисплеи.

Приложения могут согласиться, указав минимальный поддерживаемый размер, который меньше или равен целевому размеру дисплея. Для этого используйте атрибуты макета активности android:minHeight и android:minWidth в AndroidManifest.

Политики отображения

Android 10 отделяет и перемещает определенные политики отображения из реализации WindowManagerPolicy по умолчанию в PhoneWindowManager в классы для каждого дисплея, например:

  • Состояние отображения и поворот
  • Некоторые клавиши и отслеживание событий движения
  • Системный интерфейс и декоративные окна

В Android 9 (и более ранних версиях) класс PhoneWindowManager обрабатывал политики отображения, состояние и настройки, вращение, отслеживание рамки декоративного окна и многое другое. В Android 10 большая часть этого перенесена в класс DisplayPolicy , за исключением отслеживания вращения, которое было перенесено в DisplayRotation .

Настройки окна дисплея

В Android 10 настраиваемые параметры окон для каждого дисплея были расширены и теперь включают:

  • Оконный режим отображения по умолчанию
  • Значения пересканирования
  • Ротация пользователя и режим ротации
  • Принудительный размер, плотность и режим масштабирования
  • Режим удаления контента (когда дисплей удален)
  • Поддержка системных украшений и IME

Класс DisplayWindowSettings содержит настройки для этих параметров. Они сохраняются на диске в разделе /data в display_settings.xml каждый раз при изменении параметра. Дополнительные сведения см. в разделах DisplayWindowSettings.AtomicFileStorage и DisplayWindowSettings#writeSettings() . Производители устройств могут указать значения по умолчанию в display_settings.xml для конфигурации своих устройств. Однако, поскольку файл хранится в /data , для восстановления файла в случае его удаления может потребоваться дополнительная логика.

По умолчанию Android 10 использует DisplayInfo#uniqueId в качестве идентификатора дисплея при сохранении настроек. uniqueId должен быть заполнен для всех дисплеев. Кроме того, он стабилен для физических и сетевых дисплеев. Также можно использовать порт физического дисплея в качестве идентификатора, который можно установить в DisplayWindowSettings#mIdentifier . При каждой записи записываются все настройки, поэтому можно безопасно обновить ключ, используемый для отображаемой записи в хранилище. Подробности см. в разделе Статические идентификаторы отображения .

Настройки сохраняются в каталоге /data по историческим причинам. Первоначально они использовались для сохранения установленных пользователем настроек, таких как поворот дисплея.

Статические идентификаторы отображения

Android 9 (и более ранние версии) не обеспечивал стабильных идентификаторов дисплеев в рамках. Когда дисплей добавлялся в систему, для него создавались Display#mDisplayId или DisplayInfo#displayId путем увеличения статического счетчика. Если система добавляла и удаляла один и тот же дисплей, получался другой идентификатор.

Если на устройстве было несколько дисплеев, доступных при загрузке, дисплеям могли быть назначены разные идентификаторы в зависимости от времени. Хотя Android 9 (и более ранние версии) включал DisplayInfo#uniqueId , он не содержал достаточно информации для различения дисплеев, поскольку физические дисплеи были идентифицированы как local:0 или local:1 , чтобы представлять встроенный и внешний дисплей.

В Android 10 изменен DisplayInfo#uniqueId , чтобы добавить стабильный идентификатор и различать локальные, сетевые и виртуальные дисплеи.

Тип дисплея Формат
Местный
local:<stable-id>
Сеть
network:<mac-address>
Виртуальный
virtual:<package-name-and-name>

В дополнение к обновлениям uniqueId DisplayInfo.address содержит DisplayAddress — идентификатор отображения, который остается стабильным при перезагрузках. В Android 10 DisplayAddress поддерживает физические и сетевые дисплеи. DisplayAddress.Physical содержит стабильный идентификатор дисплея (тот же, что и uniqueId ) и может быть создан с помощью DisplayAddress#fromPhysicalDisplayId() .

Android 10 также предоставляет удобный метод получения информации о порте ( Physical#getPort() ). Этот метод можно использовать в рамках статической идентификации дисплеев. Например, он используется в DisplayWindowSettings ). DisplayAddress.Network содержит MAC-адрес и может быть создан с помощью DisplayAddress#fromMacAddress() .

Эти дополнения позволяют производителям устройств идентифицировать дисплеи в статических конфигурациях с несколькими дисплеями и настраивать различные параметры и функции системы, используя статические идентификаторы дисплеев, такие как порты для физических дисплеев. Эти методы скрыты и предназначены только для использования внутри system_server .

Учитывая идентификатор дисплея HWC (который может быть непрозрачным и не всегда стабильным), этот метод возвращает (зависит от платформы) 8-битный номер порта, который идентифицирует физический разъем для вывода дисплея, а также большой двоичный объект EDID дисплея. SurfaceFlinger извлекает информацию о производителе или модели из EDID для создания стабильных 64-битных идентификаторов дисплея, доступных для платформы. Если этот метод не поддерживается или возникает ошибка, SurfaceFlinger возвращается к устаревшему режиму MD, где DisplayInfo#address имеет значение null, а DisplayInfo#uniqueId жестко запрограммировано, как описано выше.

Чтобы убедиться, что эта функция поддерживается, запустите:

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

Используйте более двух дисплеев

В Android 9 (и более ранних версиях) SurfaceFlinger и DisplayManagerService предполагали наличие не более двух физических дисплеев с жестко закодированными идентификаторами 0 и 1.

Начиная с Android 10, SurfaceFlinger может использовать API Hardware Composer (HWC) для генерации стабильных идентификаторов дисплеев, что позволяет ему управлять произвольным количеством физических дисплеев. Дополнительные сведения см. в разделе Статические идентификаторы отображения .

Платформа может искать токен IBinder для физического дисплея через SurfaceControl#getPhysicalDisplayToken после получения 64-битного идентификатора дисплея из SurfaceControl#getPhysicalDisplayIds или из события горячего подключения DisplayEventReceiver .

В Android 10 (и более ранних версиях) основным внутренним дисплеем является TYPE_INTERNAL , а все дополнительные дисплеи помечаются как TYPE_EXTERNAL независимо от типа подключения. Поэтому дополнительные внутренние дисплеи считаются внешними. В качестве обходного пути код, специфичный для устройства, может делать предположения относительно DisplayAddress.Physical#getPort , если HWC известен и логика выделения портов предсказуема.

Это ограничение снято в Android 11 (и более поздних версиях).

  • В Android 11 первый дисплей, отображаемый во время загрузки, является основным дисплеем. Тип подключения (внутреннее или внешнее) не имеет значения. Однако остается верным, что основной дисплей не может быть отключен, и из этого следует, что на практике он должен быть внутренним дисплеем. Обратите внимание, что некоторые складные телефоны имеют несколько внутренних дисплеев.
  • Вторичные дисплеи правильно классифицируются как Display.TYPE_INTERNAL или Display.TYPE_EXTERNAL (ранее известные как Display.TYPE_BUILT_IN и Display.TYPE_HDMI соответственно) в зависимости от типа их подключения.

Выполнение

В Android 9 и более ранних версиях дисплеи идентифицируются по 32-битным идентификаторам, где 0 — внутренний дисплей, 1 — внешний дисплей, [2, INT32_MAX] — виртуальные дисплеи HWC, а -1 представляет собой недопустимый дисплей или виртуальный дисплей, отличный от HWC.

Начиная с Android 10, дисплеям присваиваются стабильные и постоянные идентификаторы, что позволяет SurfaceFlinger и DisplayManagerService отслеживать более двух дисплеев и распознавать ранее просмотренные дисплеи. Если HWC поддерживает IComposerClient.getDisplayIdentificationData и предоставляет данные идентификации дисплея, SurfaceFlinger анализирует структуру EDID и выделяет стабильные 64-битные идентификаторы дисплея для физических и виртуальных дисплеев HWC. Идентификаторы выражаются с использованием типа параметра, где нулевое значение представляет недопустимый дисплей или виртуальный дисплей, отличный от HWC. Без поддержки HWC SurfaceFlinger возвращается к устаревшему поведению с максимум двумя физическими дисплеями.

Фокус на каждом дисплее

Для поддержки нескольких источников входного сигнала, ориентированных на отдельные дисплеи одновременно, Android 10 можно настроить на поддержку нескольких сфокусированных окон, не более одного на каждый дисплей. Это предназначено только для особых типов устройств, когда несколько пользователей одновременно взаимодействуют с одним и тем же устройством и используют разные методы или устройства ввода, например Android Automotive.

Настоятельно рекомендуется не включать эту функцию для обычных устройств, включая многоэкранные устройства или устройства, используемые для работы наподобие настольных компьютеров. Это связано прежде всего с проблемами безопасности, которые могут заставить пользователей задаться вопросом, какое окно имеет фокус ввода.

Представьте себе пользователя, который вводит защищенную информацию в поле ввода текста, например, входит в банковское приложение или вводит текст, содержащий конфиденциальную информацию. Вредоносное приложение может создать виртуальный закадровый дисплей для выполнения действий, также с полем для ввода текста. Законные и вредоносные действия имеют фокус, и оба отображают активный индикатор ввода (мигающий курсор).

Однако, поскольку ввод с клавиатуры (аппаратной или программной) вводится только в самое верхнее действие (то приложение, которое было запущено последним), путем создания скрытого виртуального дисплея вредоносное приложение может перехватить ввод пользователя, даже при использовании программной клавиатуры на основном дисплее устройства.

Используйте com.android.internal.R.bool.config_perDisplayFocusEnabled , чтобы установить фокус для каждого дисплея.

Совместимость

Проблема. В Android 9 и более ранних версиях фокус одновременно находится не более чем на одном окне системы.

Решение. В том редком случае, когда два окна одного и того же процесса будут сфокусированы, система фокусирует только то окно, которое находится выше в Z-порядке. Это ограничение удаляется для приложений, которые нацелены на Android 10, после чего ожидается, что они могут поддерживать несколько окон, сфокусированных на одновременно.

Выполнение

WindowManagerService#mPerDisplayFocusEnabled управляет доступностью этой функции. В ActivityManager ActivityDisplay#getFocusedStack() теперь используется вместо глобального отслеживания в переменной. ActivityDisplay#getFocusedStack() определяет фокус на основе Z-порядка вместо кэширования значения. Это так, что только один источник, Windowmanager, нуждается в отслеживании Z-порядка действий.

ActivityStackSupervisor#getTopDisplayFocusedStack() использует аналогичный подход для тех случаев, когда должен быть идентифицирован самый верхний сфокусированный стек в системе. Стеки пересекаются сверху вниз, ища первого подходящего стека.

InputDispatcher теперь может иметь несколько сфокусированных окон (по одному на дисплей). Если входное событие является специфичным для отображения, то оно отправляется в сфокусированное окно на соответствующем дисплее. В противном случае он отправляется в сфокусированное окно на сфокусированном дисплее, который представляет собой дисплей, с которым пользователь совсем недавно взаимодействовал.

См. InputDispatcher::mFocusedWindowHandlesByDisplay и InputDispatcher::setFocusedDisplay() . Сфокусированные приложения также обновляются отдельно в Inpint ManagerService через NativeInputManager::setFocusedApplication() .

В WindowManager сфокусированные окна также отслеживаются отдельно. См. DisplayContent#mCurrentFocus и DisplayContent#mFocusedApp и соответствующие использование. Смежные методы отслеживания фокуса и обновления были перемещены от WindowManagerService в DisplayContent .

,

Ниже приведены обновления, сделанные для этих областей, специфичных для дисплея:

Изменить размер деятельности и дисплеи

Чтобы указать, что приложение может не поддерживать режим многоналиции или изменения размера, действия используют resizeableActivity=false . Общие проблемы, с которыми сталкиваются приложения при изменении действия, включают:

  • Задание может иметь другую конфигурацию из приложения или другого невизуального компонента. Распространенной ошибкой является чтение показателей отображения из контекста приложения. Возвращенные значения не будут скорректированы на видимые метрики области, в которых отображается деятельность.
  • Задание может не обрабатывать изменение размера и сбоя, отображать искаженный пользовательский интерфейс или потерять состояние из -за повторного запуска без сохранения состояния экземпляра.
  • Приложение может попытаться использовать абсолютные координаты ввода (вместо тех, которые относительно положения окна), которые могут сломать вход в многопользовательский.

В Android 7 (и выше) приложение может быть установлено resizeableActivity=false , чтобы всегда работать в полном режиме. В этом случае платформа предотвращает нерепортизируемые виды деятельности в разделение экрана. Если пользователь пытается вызвать нерезидентскую деятельность из запуска, в то время как уже в режиме разделенного экрана, платформа выходит из режима разделенного экрана и запускает нерезидентную деятельность в полноэкранном режиме.

Приложения, которые явно устанавливают этот атрибут на false в манифесте, не должны быть запущены в режиме с несколькими окнами, если не применяется режим совместимости:

  • Та же конфигурация применяется к процессу, который содержит все действия и компоненты неактивности.
  • Примененная конфигурация соответствует требованиям CDD для совместимых с приложениями дисплеев.

В Android 10 платформа по-прежнему предотвращает нерезиденты действий в режиме разделенного экрана, но они могут быть временно масштабированы, если деятельность объявила фиксированную ориентацию или соотношение сторон. Если нет, деятельность изменяется, чтобы заполнить весь экран, как в Android 9 и ниже.

Реализация по умолчанию применяет следующую политику:

Когда действие, объявленное несовместимым с мульти-Window с использованием атрибута android:resizeableActivity , и когда эта деятельность соответствует одному из условий, описанных ниже, тогда, когда применяемый конфигурация экрана должна измениться, действие и процесс сохраняются с исходной конфигурацией, а пользователь предоставляется предоставление для перезапуска процесса приложения для использования обновленной конфигурации экрана.

  • Фиксированная ориентация с помощью приложения android:screenOrientation
  • Приложение имеет максимальное или минимальное соотношение сторон путем нацеливания на уровень API или явно объявляет соотношение сторон

На этом рисунке отображается нерезидентная деятельность с объявленным соотношением сторон. При складывании устройства окно масштабируется вниз, чтобы соответствовать области при сохранении соотношения сторон, используя соответствующую почтовую коробку. Кроме того, для пользователя предоставляется опция активности перезапуска каждый раз, когда изменяется область отображения для деятельности.

При развертывании устройства конфигурация, размер и соотношение сторон не меняются, но отображается возможность перезагрузки деятельности.

Когда resizeableActivity не установлена ​​(или оно установлено true ), приложение полностью поддерживает изменение размера.

Выполнение

Нерезидентная деятельность с фиксированной ориентацией или соотношением сторон называется режим совместимости размера (SCM) в коде. Условие определяется в ActivityRecord#shouldUseSizeCompatMode() . Когда запускается активность SCM, конфигурация, связанная с экраном (например, размер или плотность) зафиксирована в запрашиваемой конфигурации переопределения, поэтому активность больше не зависит от текущей конфигурации дисплея.

Если активность SCM не может заполнить весь экран, она выровнен в верхней части и горизонтально центрирована. Границы активности вычисляются с помощью AppWindowToken#calculateCompatBoundsTransformation() .

Когда активность SCM использует другую конфигурацию экрана, чем его контейнер (например, дисплей изменяется, или активность перемещается на другой дисплей), ActivityRecord#inSizeCompatMode() является истинной, а SizeCompatModeActivityController (в системном пользовательском интерфейсе) получает обратный вызов, чтобы показать кнопку процесса.

Размеры отображения и соотношения сторон

Android 10 обеспечивает поддержку новых соотношений сторон от высоких соотношений длинных и тонких экранов до 1: 1. Приложения могут определить ApplicationInfo#maxAspectRatio и ApplicationInfo#minAspectRatio на экране, с которым они могут обрабатывать.

Коэффициенты приложений в Android 10

Рисунок 1. Пример соотношения приложений, поддерживаемых в Android 10

Реализации устройств могут иметь вторичные дисплеи с размерами и разрешениями меньше, чем те, которые требуются для Android 9, и ниже (минимум на 2,5 дюйма или высота, минимум 320 DP для smallestScreenWidth ), но только действия, которые выбирают для поддержки этих небольших дисплеев, могут быть размещены там.

Приложения могут выбрать, объявив минимальный поддерживаемый размер, который меньше или равен целевому размеру дисплея. Используйте android:minHeight и android:minWidth в AndroidManifest для этого.

Политики отображения

Android 10 отделяет и перемещает определенные политики отображения от реализации WindowManagerPolicy в PhoneWindowManager на классы для каждого диска, например:

  • Состояние отображения и вращение
  • Некоторые ключи и отслеживание событий движения
  • Системный пользовательский интерфейс и окна украшения

В Android 9 (и нижнем) класс PhoneWindowManager обрабатывал политики отображения, состояние и настройки, вращение, отслеживание окна отделки и многое другое. Android 10 перемещает большую часть этого в класс DisplayPolicy , за исключением отслеживания вращения, которое было перенесено на DisplayRotation .

Отображать настройки окна

В Android 10 настраиваемая настройка окна на расстоянии была расширена, чтобы включить:

  • Режим отображения по умолчанию
  • Значения Overscan
  • Режим вращения пользователя и вращения
  • Принудительный размер, плотность и режим масштабирования
  • Режим удаления контента (при удалении отображения)
  • Поддержка системных украшений и IME

Класс DisplayWindowSettings содержит настройки для этих параметров. Они сохраняются, чтобы диск /data в display_settings.xml Каждый раз, когда изменяется настройка. Для получения подробной информации см. DisplayWindowSettings.AtomicFileStorage и DisplayWindowSettings#writeSettings() . Производители устройств могут предоставлять значения по умолчанию в display_settings.xml для конфигурации их устройства. Однако, поскольку файл хранится в /data , может потребоваться дополнительная логика для восстановления файла, если он стерт с помощью Wipe.

По умолчанию Android 10 использует DisplayInfo#uniqueId в качестве идентификатора для дисплея при сохранении настроек. uniqueId должен быть заполнен для всех дисплеев. Кроме того, он стабилен для физических и сетевых дисплеев. Также можно использовать порт физического дисплея в качестве идентификатора, который можно установить в DisplayWindowSettings#mIdentifier . После каждой записи все настройки записываются, поэтому безопасно обновить ключ, который используется для записи дисплея в хранилище. Подробнее см. Статические идентификаторы дисплея .

Настройки сохраняются в каталоге /data по историческим причинам. Первоначально они использовались для сохранения настройки набора пользователей, таких как вращение дисплея.

Статические идентификаторы дисплея

Android 9 (и нижний) не предоставил стабильные идентификаторы для дисплеев в рамках. Когда дисплей был добавлен в систему, для этого Display#mDisplayId или DisplayInfo#displayId путем увеличения статического счетчика. Если система добавила и сняла один и тот же дисплей, в результате появился другой идентификатор.

Если у устройства было несколько дисплеев, доступных на загрузке, дисплеям можно было бы назначить разные идентификаторы, в зависимости от времени. В то время как Android 9 (и раньше) включал DisplayInfo#uniqueId , он не содержал достаточного количества информации, чтобы различать дисплеи, потому что физические дисплеи были идентифицированы как local:0 или local:1 , для представления встроенного и внешнего дисплея.

Android 10 изменяет DisplayInfo#uniqueId , чтобы добавить стабильный идентификатор и различать локальные, сетевые и виртуальные дисплеи.

Дисплей тип Формат
Местный
local:<stable-id>
Сеть
network:<mac-address>
Виртуальный
virtual:<package-name-and-name>

В дополнение к обновлениям до uniqueId , DisplayInfo.address содержит DisplayAddress , идентификатор дисплея, который устойчив в результате перезагрузки. В Android 10 DisplayAddress поддерживает физические и сетевые дисплеи. DisplayAddress.Physical содержит стабильный идентификатор дисплея (такой же, как у uniqueId ) и может быть создан с помощью DisplayAddress#fromPhysicalDisplayId() .

Android 10 также предоставляет удобный метод получения информации о порте ( Physical#getPort() ). Этот метод можно использовать в рамках для статической идентификации дисплеев. Например, он используется в DisplayWindowSettings ). DisplayAddress.Network содержит MAC -адрес и может быть создан с помощью DisplayAddress#fromMacAddress() .

Эти дополнения позволяют производителям устройств идентифицировать дисплеев в статических настройках с несколькими различиями и настраивать различные настройки и функции системы с использованием статических идентификаторов дисплея, таких как порты для физических дисплеев. Эти методы скрыты и предназначены только для использования в system_server .

Учитывая идентификатор дисплея HWC (который может быть непрозрачным и не всегда стабильным), этот метод возвращает (специфичный для платформы) 8-битный номер порта, который идентифицирует физический разъем для вывода дисплея, а также Blob Display Edid. SurfaceFlinger извлекает производителя или информацию о модели из EDID, чтобы генерировать стабильные 64-разрядные идентификаторы дисплея, подвергшиеся воздействию структуры. Если этот метод не поддерживается или не подтверждается, Surfaceflinger возвращается в режим Legacy MD, где DisplayInfo#address является NULL, а DisplayInfo#uniqueId жестко кодируется, как описано выше.

Чтобы убедиться, что эта функция поддерживается, запустите:

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

Используйте более двух дисплеев

В Android 9 (и ниже) Surfaceflinger и DisplayManagerService предположили существование не более двух физических дисплеев с твердыми идентификаторами 0 и 1.

Начиная с Android 10, Surfaceflinger может использовать API аппаратного композитора (HWC) для генерации стабильных идентификаторов дисплея, что позволяет ему управлять произвольным количеством физических дисплеев. Чтобы узнать больше, см. Статические идентификаторы дисплея .

Фреймворк может искать токен IBinder для физического дисплея через SurfaceControl#getPhysicalDisplayToken после получения 64-битного идентификатора дисплея от SurfaceControl#getPhysicalDisplayIds или от события Hotplug DisplayEventReceiver .

В Android 10 (и ниже) основной внутренний дисплей является TYPE_INTERNAL , и все вторичные дисплеи помечены как TYPE_EXTERNAL независимо от типа соединения. Следовательно, дополнительные внутренние дисплеи рассматриваются как внешние. В качестве обходного пути код, специфичный для устройства, может сделать предположения о DisplayAddress.Physical#getPort , если HWC известен и логика распределения портов предсказуем.

Это ограничение удалено в Android 11 (и выше).

  • В Android 11 первый дисплей, о котором сообщается во время загрузки, является основным дисплеем. Тип соединения (внутренний и внешний) не имеет значения. Тем не менее, остается верно, что основной дисплей не может быть отключен и следует, что на практике он должен быть внутренним дисплеем. Обратите внимание, что некоторые складные телефоны имеют несколько внутренних дисплеев.
  • Вторичные дисплеи правильно классифицируются как Display.TYPE_INTERNAL или Display.TYPE_EXTERNAL (ранее известный как Display.TYPE_BUILT_IN и Display.TYPE_HDMI , соответственно) в зависимости от их типа соединения.

Выполнение

В Android 9 и нижнем, дисплеи идентифицируются 32-битными идентификаторами, где 0-внутренний дисплей, 1-внешний дисплей, [2, INT32_MAX] виртуальные дисплеи HWC, а -1 представляет неверный дисплей или виртуальный дисплей без HWC.

Начиная с Android 10, дисплеи дают стабильные и постоянные идентификаторы, что позволяет Surfaceflinger и DisplayManagerService отслеживать более двух дисплеев и распознавать ранее виденные дисплеи. Если HWC поддерживает IComposerClient.getDisplayIdentificationData и предоставляет данные идентификации отображения, SurfaceFlinger анализирует структуру edid и выделяет стабильные 64-разрядные идентификаторы дисплея для физических и виртуальных дисплеев HWC. Идентификаторы выражаются с использованием типа опции, где нулевое значение представляет собой неверный дисплей или виртуальный дисплей без HWC. Без поддержки HWC Surfaceflinger возвращается к устаревшему поведению с двумя физическими дисплеями.

Фокус на предмет

Чтобы поддержать несколько входных источников, которые нацелены на отдельные отображения одновременно, Android 10 может быть настроен для поддержки нескольких целенаправленных окон, не более одного на размер. Это предназначено только для специальных типов устройств, когда несколько пользователей взаимодействуют с одним и тем же устройством одновременно и используют различные методы ввода или устройства, такие как Android Automotive.

Настоятельно рекомендуется, чтобы эта функция не была включена для обычных устройств, включая многоэкранные устройства или те, которые используются для настольных впечатлений. Это связано, прежде всего, с проблемой безопасности, которая может заставить пользователей задаться вопросом, какое окно имеет фокус ввода.

Представьте себе пользователя, который вводит безопасную информацию в поле текстового ввода, возможно, входит в систему в банковское приложение или введет текст, который содержит конфиденциальную информацию. Злодие приложение может создать виртуальный дисплей вне экрана, с помощью которого можно выполнить деятельность, также с полем текстового ввода. Законные и злонамеренные действия сосредоточены, и оба отображают активный индикатор ввода (мигающий курсор).

Однако, поскольку ввод с клавиатуры (оборудование или программное обеспечение) вводится только в самую верхнюю деятельность (это приложение, которое было недавно запущено), создав скрытый виртуальный дисплей, вредоносное приложение может получить пользовательский ввод, даже при использовании программной клавиатуры на основном дисплее устройства.

Используйте com.android.internal.R.bool.config_perDisplayFocusEnabled для установки фокуса на предмет.

Совместимость

Проблема: в Android 9 и ниже, не более одного окна в системе сфокусировано за раз.

Решение: в редком случае, когда будут сфокусированы два окна из одного и того же процесса, система обеспечивает фокус только на окне, которое выше в Z-порядке. Это ограничение удаляется для приложений, которые нацелены на Android 10, после чего ожидается, что они могут поддерживать несколько окон, сфокусированных на одновременно.

Выполнение

WindowManagerService#mPerDisplayFocusEnabled управляет доступностью этой функции. В ActivityManager ActivityDisplay#getFocusedStack() теперь используется вместо глобального отслеживания в переменной. ActivityDisplay#getFocusedStack() определяет фокус на основе Z-порядка вместо кэширования значения. Это так, что только один источник, Windowmanager, нуждается в отслеживании Z-порядка действий.

ActivityStackSupervisor#getTopDisplayFocusedStack() использует аналогичный подход для тех случаев, когда должен быть идентифицирован самый верхний сфокусированный стек в системе. Стеки пересекаются сверху вниз, ища первого подходящего стека.

InputDispatcher теперь может иметь несколько сфокусированных окон (по одному на дисплей). Если входное событие является специфичным для отображения, то оно отправляется в сфокусированное окно на соответствующем дисплее. В противном случае он отправляется в сфокусированное окно на сфокусированном дисплее, который представляет собой дисплей, с которым пользователь совсем недавно взаимодействовал.

См. InputDispatcher::mFocusedWindowHandlesByDisplay и InputDispatcher::setFocusedDisplay() . Сфокусированные приложения также обновляются отдельно в Inpint ManagerService через NativeInputManager::setFocusedApplication() .

В WindowManager сфокусированные окна также отслеживаются отдельно. См. DisplayContent#mCurrentFocus и DisplayContent#mFocusedApp и соответствующие использование. Смежные методы отслеживания фокуса и обновления были перемещены от WindowManagerService в DisplayContent .

,

Ниже приведены обновления, сделанные для этих областей, специфичных для дисплея:

Изменить размер деятельности и дисплеи

Чтобы указать, что приложение может не поддерживать режим многоналиции или изменения размера, действия используют resizeableActivity=false . Общие проблемы, с которыми сталкиваются приложения при изменении действия, включают:

  • Задание может иметь другую конфигурацию из приложения или другого невизуального компонента. Распространенной ошибкой является чтение показателей отображения из контекста приложения. Возвращенные значения не будут скорректированы на видимые метрики области, в которых отображается деятельность.
  • Задание может не обрабатывать изменение размера и сбоя, отображать искаженный пользовательский интерфейс или потерять состояние из -за повторного запуска без сохранения состояния экземпляра.
  • Приложение может попытаться использовать абсолютные координаты ввода (вместо тех, которые относительно положения окна), которые могут сломать вход в многопользовательский.

В Android 7 (и выше) приложение может быть установлено resizeableActivity=false , чтобы всегда работать в полном режиме. В этом случае платформа предотвращает нерепортизируемые виды деятельности в разделение экрана. Если пользователь пытается вызвать нерезидентскую деятельность из запуска, в то время как уже в режиме разделенного экрана, платформа выходит из режима разделенного экрана и запускает нерезидентную деятельность в полноэкранном режиме.

Приложения, которые явно устанавливают этот атрибут на false в манифесте, не должны быть запущены в режиме с несколькими окнами, если не применяется режим совместимости:

  • Та же конфигурация применяется к процессу, который содержит все действия и компоненты неактивности.
  • Примененная конфигурация соответствует требованиям CDD для совместимых с приложениями дисплеев.

В Android 10 платформа по-прежнему предотвращает нерезиденты действий в режиме разделенного экрана, но они могут быть временно масштабированы, если деятельность объявила фиксированную ориентацию или соотношение сторон. Если нет, деятельность изменяется, чтобы заполнить весь экран, как в Android 9 и ниже.

Реализация по умолчанию применяет следующую политику:

Когда действие, объявленное несовместимым с мульти-Window с использованием атрибута android:resizeableActivity , и когда эта деятельность соответствует одному из условий, описанных ниже, тогда, когда применяемый конфигурация экрана должна измениться, действие и процесс сохраняются с исходной конфигурацией, а пользователь предоставляется предоставление для перезапуска процесса приложения для использования обновленной конфигурации экрана.

  • Фиксированная ориентация с помощью приложения android:screenOrientation
  • Приложение имеет максимальное или минимальное соотношение сторон путем нацеливания на уровень API или явно объявляет соотношение сторон

На этом рисунке отображается нерезидентная деятельность с объявленным соотношением сторон. При складывании устройства окно масштабируется вниз, чтобы соответствовать области при сохранении соотношения сторон, используя соответствующую почтовую коробку. Кроме того, для пользователя предоставляется опция активности перезапуска каждый раз, когда изменяется область отображения для деятельности.

При развертывании устройства конфигурация, размер и соотношение сторон не меняются, но отображается возможность перезагрузки деятельности.

Когда resizeableActivity не установлена ​​(или оно установлено true ), приложение полностью поддерживает изменение размера.

Выполнение

Нерезидентная деятельность с фиксированной ориентацией или соотношением сторон называется режим совместимости размера (SCM) в коде. Условие определяется в ActivityRecord#shouldUseSizeCompatMode() . Когда запускается активность SCM, конфигурация, связанная с экраном (например, размер или плотность) зафиксирована в запрашиваемой конфигурации переопределения, поэтому активность больше не зависит от текущей конфигурации дисплея.

Если активность SCM не может заполнить весь экран, она выровнен в верхней части и горизонтально центрирована. Границы активности вычисляются с помощью AppWindowToken#calculateCompatBoundsTransformation() .

Когда активность SCM использует другую конфигурацию экрана, чем его контейнер (например, дисплей изменяется, или активность перемещается на другой дисплей), ActivityRecord#inSizeCompatMode() является истинной, а SizeCompatModeActivityController (в системном пользовательском интерфейсе) получает обратный вызов, чтобы показать кнопку процесса.

Размеры отображения и соотношения сторон

Android 10 обеспечивает поддержку новых соотношений сторон от высоких соотношений длинных и тонких экранов до 1: 1. Приложения могут определить ApplicationInfo#maxAspectRatio и ApplicationInfo#minAspectRatio на экране, с которым они могут обрабатывать.

Коэффициенты приложений в Android 10

Рисунок 1. Пример соотношения приложений, поддерживаемых в Android 10

Реализации устройств могут иметь вторичные дисплеи с размерами и разрешениями меньше, чем те, которые требуются для Android 9, и ниже (минимум на 2,5 дюйма или высота, минимум 320 DP для smallestScreenWidth ), но только действия, которые выбирают для поддержки этих небольших дисплеев, могут быть размещены там.

Приложения могут выбрать, объявив минимальный поддерживаемый размер, который меньше или равен целевому размеру дисплея. Используйте android:minHeight и android:minWidth в AndroidManifest для этого.

Политики отображения

Android 10 отделяет и перемещает определенные политики отображения от реализации WindowManagerPolicy в PhoneWindowManager на классы для каждого диска, например:

  • Состояние отображения и вращение
  • Некоторые ключи и отслеживание событий движения
  • Системный пользовательский интерфейс и окна украшения

В Android 9 (и нижнем) класс PhoneWindowManager обрабатывал политики отображения, состояние и настройки, вращение, отслеживание окна отделки и многое другое. Android 10 перемещает большую часть этого в класс DisplayPolicy , за исключением отслеживания вращения, которое было перенесено на DisplayRotation .

Отображать настройки окна

В Android 10 настраиваемая настройка окна на расстоянии была расширена, чтобы включить:

  • Режим отображения по умолчанию
  • Значения Overscan
  • Режим вращения пользователя и вращения
  • Принудительный размер, плотность и режим масштабирования
  • Режим удаления контента (при удалении отображения)
  • Поддержка системных украшений и IME

Класс DisplayWindowSettings содержит настройки для этих параметров. Они сохраняются, чтобы диск /data в display_settings.xml Каждый раз, когда изменяется настройка. Для получения подробной информации см. DisplayWindowSettings.AtomicFileStorage и DisplayWindowSettings#writeSettings() . Производители устройств могут предоставлять значения по умолчанию в display_settings.xml для конфигурации их устройства. Однако, поскольку файл хранится в /data , может потребоваться дополнительная логика для восстановления файла, если он стерт с помощью Wipe.

По умолчанию Android 10 использует DisplayInfo#uniqueId в качестве идентификатора для дисплея при сохранении настроек. uniqueId должен быть заполнен для всех дисплеев. Кроме того, он стабилен для физических и сетевых дисплеев. Также можно использовать порт физического дисплея в качестве идентификатора, который можно установить в DisplayWindowSettings#mIdentifier . После каждой записи все настройки записываются, поэтому безопасно обновить ключ, который используется для записи дисплея в хранилище. Подробнее см. Статические идентификаторы дисплея .

Настройки сохраняются в каталоге /data по историческим причинам. Первоначально они использовались для сохранения настройки набора пользователей, таких как вращение дисплея.

Статические идентификаторы дисплея

Android 9 (и нижний) не предоставил стабильные идентификаторы для дисплеев в рамках. Когда дисплей был добавлен в систему, для этого Display#mDisplayId или DisplayInfo#displayId путем увеличения статического счетчика. Если система добавила и сняла один и тот же дисплей, в результате появился другой идентификатор.

Если у устройства было несколько дисплеев, доступных на загрузке, дисплеям можно было бы назначить разные идентификаторы, в зависимости от времени. В то время как Android 9 (и раньше) включал DisplayInfo#uniqueId , он не содержал достаточного количества информации, чтобы различать дисплеи, потому что физические дисплеи были идентифицированы как local:0 или local:1 , для представления встроенного и внешнего дисплея.

Android 10 изменяет DisplayInfo#uniqueId , чтобы добавить стабильный идентификатор и различать локальные, сетевые и виртуальные дисплеи.

Дисплей тип Формат
Местный
local:<stable-id>
Сеть
network:<mac-address>
Виртуальный
virtual:<package-name-and-name>

В дополнение к обновлениям до uniqueId , DisplayInfo.address содержит DisplayAddress , идентификатор дисплея, который устойчив в результате перезагрузки. В Android 10 DisplayAddress поддерживает физические и сетевые дисплеи. DisplayAddress.Physical содержит стабильный идентификатор дисплея (такой же, как у uniqueId ) и может быть создан с помощью DisplayAddress#fromPhysicalDisplayId() .

Android 10 также предоставляет удобный метод получения информации о порте ( Physical#getPort() ). Этот метод можно использовать в рамках для статической идентификации дисплеев. Например, он используется в DisplayWindowSettings ). DisplayAddress.Network содержит MAC -адрес и может быть создан с помощью DisplayAddress#fromMacAddress() .

Эти дополнения позволяют производителям устройств идентифицировать дисплеев в статических настройках с несколькими различиями и настраивать различные настройки и функции системы с использованием статических идентификаторов дисплея, таких как порты для физических дисплеев. Эти методы скрыты и предназначены только для использования в system_server .

Учитывая идентификатор дисплея HWC (который может быть непрозрачным и не всегда стабильным), этот метод возвращает (специфичный для платформы) 8-битный номер порта, который идентифицирует физический разъем для вывода дисплея, а также Blob Display Edid. Surfaceflinger извлекает производителя или информацию о модели из EDID, чтобы генерировать стабильные 64-разрядные идентификаторы дисплея, подвергшиеся воздействию структуры. Если этот метод не поддерживается или не подтверждается, Surfaceflinger возвращается в режим Legacy MD, где DisplayInfo#address является NULL, а DisplayInfo#uniqueId жестко кодируется, как описано выше.

Чтобы убедиться, что эта функция поддерживается, запустите:

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

Используйте более двух дисплеев

В Android 9 (и ниже) Surfaceflinger и DisplayManagerService предположили существование не более двух физических дисплеев с твердыми идентификаторами 0 и 1.

Начиная с Android 10, Surfaceflinger может использовать API аппаратного композитора (HWC) для генерации стабильных идентификаторов дисплея, что позволяет ему управлять произвольным количеством физических дисплеев. Чтобы узнать больше, см. Статические идентификаторы дисплея .

Фреймворк может искать токен IBinder для физического дисплея через SurfaceControl#getPhysicalDisplayToken после получения 64-битного идентификатора дисплея от SurfaceControl#getPhysicalDisplayIds или от события Hotplug DisplayEventReceiver .

В Android 10 (и ниже) основной внутренний дисплей является TYPE_INTERNAL , и все вторичные дисплеи помечены как TYPE_EXTERNAL независимо от типа соединения. Следовательно, дополнительные внутренние дисплеи рассматриваются как внешние. В качестве обходного пути код, специфичный для устройства, может сделать предположения о DisplayAddress.Physical#getPort , если HWC известен и логика распределения портов предсказуем.

Это ограничение удалено в Android 11 (и выше).

  • В Android 11 первый дисплей, о котором сообщается во время загрузки, является основным дисплеем. Тип соединения (внутренний и внешний) не имеет значения. Тем не менее, остается верно, что основной дисплей не может быть отключен и следует, что на практике он должен быть внутренним дисплеем. Обратите внимание, что некоторые складные телефоны имеют несколько внутренних дисплеев.
  • Вторичные дисплеи правильно классифицируются как Display.TYPE_INTERNAL или Display.TYPE_EXTERNAL (ранее известный как Display.TYPE_BUILT_IN и Display.TYPE_HDMI , соответственно) в зависимости от их типа соединения.

Выполнение

В Android 9 и нижнем, дисплеи идентифицируются 32-битными идентификаторами, где 0-внутренний дисплей, 1-внешний дисплей, [2, INT32_MAX] виртуальные дисплеи HWC, а -1 представляет неверный дисплей или виртуальный дисплей без HWC.

Начиная с Android 10, дисплеи дают стабильные и постоянные идентификаторы, что позволяет Surfaceflinger и DisplayManagerService отслеживать более двух дисплеев и распознавать ранее виденные дисплеи. Если HWC поддерживает IComposerClient.getDisplayIdentificationData и предоставляет данные идентификации отображения, SurfaceFlinger анализирует структуру edid и выделяет стабильные 64-разрядные идентификаторы дисплея для физических и виртуальных дисплеев HWC. Идентификаторы выражаются с использованием типа опции, где нулевое значение представляет собой неверный дисплей или виртуальный дисплей без HWC. Без поддержки HWC Surfaceflinger возвращается к устаревшему поведению с двумя физическими дисплеями.

Фокус на предмет

To support several input sources that target individual displays at the same time, Android 10 can be configured to support multiple focused windows, at most one per-display. This is intended only for special types of devices when multiple users interact with the same device at the same time and use different input methods or devices, such as Android Automotive.

It is strongly recommended that this feature not be enabled for regular devices, including multi-screen devices or those used for desktop-like experiences. This is due primarily to a security concern that may cause users to wonder which window has input focus.

Imagine the user who enters secure information into a text input field, perhaps logging in to a banking app or entering text that contains sensitive information. A malicious app could create a virtual off-screen display with which to execute an activity, also with a text input field. Legitimate and malicious activities have focus and both display an active input indicator (blinking cursor).

However, since input from a keyboard (hardware or software) is entered into the topmost activity only (that app that was most recently launched), by creating a hidden virtual display, a malicious app could grab user input, even when using a software keyboard on the primary device display.

Use com.android.internal.R.bool.config_perDisplayFocusEnabled to set per-display focus.

Совместимость

Issue: In Android 9 and lower, at most one window in the system has focus at a time.

Solution: In the rare case when two windows from the same process would be focused, the system provides focus only to the window that's higher in the Z-order. This restriction is removed for apps that target Android 10, at which point it's expected that they can support multiple windows being focused on simultaneously.

Выполнение

WindowManagerService#mPerDisplayFocusEnabled controls the availability of this feature. In ActivityManager , ActivityDisplay#getFocusedStack() is now used instead of global tracking in a variable. ActivityDisplay#getFocusedStack() determines focus based on Z-order instead of caching the value. This is so that only one source, WindowManager, need track the Z-order of activities.

ActivityStackSupervisor#getTopDisplayFocusedStack() takes a similar approach for those cases when the topmost focused stack in the system must be identified. The stacks are traversed from top to bottom, searching for the first eligible stack.

InputDispatcher can now have multiple focused windows (one per display). If an input event is display-specific, then it's dispatched to the focused window in the corresponding display. Otherwise, it's dispatched to the focused window in the focused display, which is the display that the user most recently interacted with.

See InputDispatcher::mFocusedWindowHandlesByDisplay and InputDispatcher::setFocusedDisplay() . Focused apps are also updated separately in InputManagerService through NativeInputManager::setFocusedApplication() .

In WindowManager , focused windows are also tracked separately. See DisplayContent#mCurrentFocus and DisplayContent#mFocusedApp and the respective uses. Related focus tracking and updating methods have been moved from WindowManagerService to DisplayContent .