目录
[1. 刷新率和帧率](#1. 刷新率和帧率)
[2. 多种刷新率](#2. 多种刷新率)
[3. 基本原理](#3. 基本原理)
[3.1 屏幕 & 显示控制器](#3.1 屏幕 & 显示控制器)
[3.2 Composer Service](#3.2 Composer Service)
[4. Framework 策略](#4. Framework 策略)
[4.2 刷新率设置项的定义](#4.2 刷新率设置项的定义)
[4.2.1 最低刷新率](#4.2.1 最低刷新率)
[4.2.2 默认刷新率 & 默认的用户设置刷新率](#4.2.2 默认刷新率 & 默认的用户设置刷新率)
[4.2.2.1 设置入口](#4.2.2.1 设置入口)
[4.2.2.2 设置场景](#4.2.2.2 设置场景)
[4.2.2.3 DisplayDeviceConfig](#4.2.2.3 DisplayDeviceConfig)
[4.2.3 用户设置的刷新率](#4.2.3 用户设置的刷新率)
[4.2.4 应用刷新率 & 应用期望的 base mode 刷新率](#4.2.4 应用刷新率 & 应用期望的 base mode 刷新率)
[4.3 刷新率更新 & 应用流程](#4.3 刷新率更新 & 应用流程)
[4.3.1 入口](#4.3.1 入口)
[4.3.1.1 system settings "peak_refresh_rate"](#4.3.1.1 system settings "peak_refresh_rate")
[4.3.1.2 device_config <"display_manager", "peak_refresh_rate_default">](#4.3.1.2 device_config <"display_manager", "peak_refresh_rate_default">)
[4.3.1.3 setDefaultRefreshRate 方法设置 "默认刷新率"(Just for test)](#4.3.1.3 setDefaultRefreshRate 方法设置 "默认刷新率"(Just for test))
[4.3.1.4 窗口切换](#4.3.1.4 窗口切换)
[4.3.1.5 小结](#4.3.1.5 小结)
[4.3.2 Vote & VotesStorage](#4.3.2 Vote & VotesStorage)
[4.3.2.1 Vote](#4.3.2.1 Vote)
[4.3.2.2 VotesStorage](#4.3.2.2 VotesStorage)
[4.3.3 DisplayModeDirector.updateRefreshRateSettingLocked](#4.3.3 DisplayModeDirector.updateRefreshRateSettingLocked)
[4.3.4 DisplayModeDirector .notifyDesiredDisplayModeSpecsChangedLocked](#4.3.4 DisplayModeDirector .notifyDesiredDisplayModeSpecsChangedLocked)
[4.3.5 DisplayModeDirector.getDesiredDisplayModeSpecs](#4.3.5 DisplayModeDirector.getDesiredDisplayModeSpecs)
[4.3.6 nativeSetDesiredDisplayModeSpecs](#4.3.6 nativeSetDesiredDisplayModeSpecs)
[4.3.7 mode 与 VoteSummary 匹配规则](#4.3.7 mode 与 VoteSummary 匹配规则)
[5. 用户设置界面](#5. 用户设置界面)
[6. 默认配置](#6. 默认配置)
1. 刷新率和帧率
帧率是指应用 1 秒绘制了多少帧。帧率一般是实时变化的,即每秒都可能不一样,上一秒 58 下一秒 60 很平常。
刷新率一般是指屏幕刷新率,即屏幕 1 秒内刷新多少次。屏幕刷新率一般是固定的,不会上一秒 58 下一秒 60;现在通常的刷新率有 60/90/120。
对于 Android 平台来说,屏幕的刷新率会影响应用绘制的调度周期。一般情况下平台调度绘制的周期与屏幕刷新的周期同步,这是通过 vsync 机制来实现的。
考虑以下两个场景:
- 游戏场景
对于游戏这类持续绘制的应用场景,我们期望帧率能够稳定在一个比较高(接近刷新率)的水平。
但是如果平台性能较差,或者游戏的算力开销较高,游戏帧率可能无法稳定在接近刷新率的水平,就会出现抖动率高的问题,用户体验会非常差。
此时如果能降低刷新率,反倒是可以使帧率更加稳定,得到更好的用户体验。
- 低功耗模式
如果我们在某些场景下期望获得更低的功耗,而这些场景对帧率并不敏感(即帧率高低不影响用户体验),降低刷新率将是不错的选择。
2. 多种刷新率
https://source.android.com/docs/core/graphics/multiple-refresh-rate?hl=zh-cn
目前,Android 11 增加了对具有多种刷新率的设备的支持。此功能包含三个主要组成部分:
-
android.hardware.graphics.composer@2.4 中引入的新 HAL API。
-
平台代码,用于解析不同刷新率的设备配置并设置所需的刷新率
-
新增的 SDK 和 NDK API,使应用可以设置所需的帧速率
这里的 "多种刷新率" 支持,包含了两层含义。
一是指屏幕的多种显示模式具有不同刷新率,而平台能够支持设置不同的显示模式;
二是指在屏幕刷新率固定的情况下,平台能够使用不同的频率来调度应用绘制。
本文为了方便区分这两种情况,定义
-
屏幕刷新率(refresh rate):屏幕刷新率是屏幕的物理属性
-
应用刷新率(render frame rate):应用绘制的调度频率,是平台软件属性
-
刷新率:包含屏幕刷新率和应用刷新率
需要注意的是,设置应用刷新率时,必须保证屏幕刷新率能被应用刷新率整除。
这是因为屏幕刷新率不能被应用刷新率整除时,帧的间隔将不均匀;而眼睛和大脑期望看到的是平滑、连续的运动,当帧显示时间不均匀时,会产生跳动感,即抖动。
一般,如果屏幕刷新率能被应用刷新率整除,我们就称应用刷新率与屏幕刷新率同步(而非完全相同)。
3. 基本原理
刷新率设置流程,涉及
-
硬件,即屏幕和显示控制器
-
Hal Service,即 composer servcie
-
SurfaceFlinger
-
Framework,即系统服务
-
应用
数据流向从下至上。
3.1 屏幕 & 显示 控制器
显示控制器(Display Controller)是一个连接屏幕的硬件单元,负责管理和驱动屏幕显示图像的各个方面。它通常包括以下功能:
-
帧缓冲管理:显示控制器从帧缓冲区读取图像数据,这些数据是由图形处理单元(GPU)或中央处理单元(CPU)生成并存储的。
-
信号生成:显示控制器生成合适的信号来驱动屏幕。这些信号包括水平和垂直同步信号、时钟信号以及数据传输信号。
-
分辨率和刷新率:控制器管理屏幕的分辨率和刷新率,确保图像以正确的分辨率显示,并以适当的刷新率更新屏幕。
-
颜色空间转换:显示控制器可能需要将图像数据从一种颜色空间转换到另一种,以适应不同的显示要求。
-
图层合成:在一些高级显示控制器中,可以支持多图层合成。这意味着它可以将来自不同来源的多个图像合成到一个最终显示的图像上。这对于窗口系统、叠加窗口以及复杂的用户界面来说非常重要。
显示控制器的工作流程
-
接收数据:显示控制器从系统内存或专用的图形内存中读取图像数据。
-
处理数据:如果需要,显示控制器会对数据进行处理,例如颜色空间转换、缩放、旋转等。
-
输出信号:将处理过的数据转换为显示屏能够理解的信号格式,并通过显示接口(如HDMI、LVDS、eDP等)发送到显示屏。
也就是说,屏幕的不同刷新率是由显示控制器控制的。
显示控制器将屏幕刷新率、分辨率(height、width)等屏幕属性封装到显示模式(display mode)中。
一个显示模式就是一组屏幕属性的集合,不同显示模式包含的屏幕属性集合不会完全相同。
3.2 Composer Service
即实现 android.hardware.graphics.composer@2.4(hal 接口)的 hal 服务进程。
本地 Android 设备的 composer service 进程是 vendor.qti.hardware.display.composer-service。
bash
yudi:/system_ext/etc/rpms # lshal|grep graphic
DM,FC Y android.hardware.graphics.allocator@4.0::IAllocator/default 0/4 1382
DM,FC Y android.hardware.graphics.composer@2.1::IComposer/default 0/3 1387
DM,FC Y android.hardware.graphics.composer@2.2::IComposer/default 0/3 1387
DM,FC Y android.hardware.graphics.composer@2.3::IComposer/default 0/3 1387
DM,FC Y android.hardware.graphics.composer@2.4::IComposer/default 0/3 1387
X ? android.hardware.graphics.mapper@4.0::I*/* (/vendor/lib/hw/) (-qti-display) N/A N/A 1245 3994
X ? android.hardware.graphics.mapper@4.0::I*/* (/vendor/lib64/hw/) (-qti-display) N/A N/A 1244 1373 1387 1599 2208 3865 3959 4067 4085 4103 4112 4123 4128 4166 4374 4399 4407 4518 4629 4861 4877 4905 4923 4948 4979 4995 5203 5245 5268 5312 5327 5385 5441 5546 5650 5763 5790 5813 5883 5927 6002 6048 6131 6139 6268 6296 6335 6385 6407 6430 6631 6836 6983 7024 7062 7105 7181 7334
yudi:/system_ext/etc/rpms # ps -A|grep 1387
system 1387 1 11534632 8952 binder_wait_for_work 0 S vendor.qti.hardware.display.composer-service
composer service 是 SurfaceFlinger 和显示控制器之间的中间层,用于提供标准化的接口,使 SurfaceFlinger 不必直接与特定的显示控制器硬件通信。它作为一个服务,通过 HIDL 接口,使得图形合成与显示控制器的具体实现分离开来。
surfaceflinger、composer service 和显示控制器的具体关系为
-
SurfaceFlinger:是 Android 系统中的显示服务器,负责
-
合成来自不同应用程序的图形内容,生成最终的显示帧
-
接收来自 Framework 的显示设置,选择显示模式,选择应用刷新率
-
-
composer service:提供了与显示控制器交互的标准接口,包括
-
发送帧
-
设置显示模式
-
-
显示控制器:硬件单元,负责接收图像数据,并将其显示在屏幕上。
显示的工作流程
-
SurfaceFlinger 合成帧:SurfaceFlinger 将来自不同应用的图形内容合成为最终的显示帧。
-
通过 composer service 发送帧:SurfaceFlinger 通过 composer service 接口将合成的帧发送到显示控制器。
-
显示控制器处理帧:显示控制器接收帧数据,并生成必要的信号将其显示在屏幕上。
设置的工作流程
-
SurfaceFlinger 接收来自 Framework 的显示设置,包括默认的 display mode(base mode)、屏幕刷新率范围、应用刷新率范围、分辨率等,根据这些显示设置选择显示模式
-
通过 composer service 设置显示模式
-
显示控制器应用显示模式,比如修改屏幕分辨率、刷新率等
这里顺便说明下 composer service 和硬件合成器(hardware composer 或 hwcomposer)的关系。
在 Android 系统中,SurfaceFlinger 有硬件合成和软件合成的区别。我们需要澄清 hwcomposer 和 composer service 的角色,以及硬件合成和软件合成的概念。
SurfaceFlinger 和合成方式:
-
软件合成:
-
在软件合成模式下,SurfaceFlinger 使用 GPU 或 CPU 来合成各个图层。
-
这种方式虽然灵活,但可能会消耗更多的 CPU/GPU 资源。
-
-
硬件合成:
-
在硬件合成模式下,SurfaceFlinger 利用专门的显示控制器硬件(Hardware Composer)来合成图层。
-
这种方式更高效,因为专用硬件可以更快地处理图层合成,并减少对 CPU/GPU 的依赖,从而提高性能和省电。
-
合成的工作流程:
-
SurfaceFlinger 负责管理图层,并决定是使用硬件合成还是软件合成。
-
硬件合成:
- 如果选择硬件合成,SurfaceFlinger 通过 composer service 接口(如 android.hardware.graphics.composer@2.4)与硬件合成器进行通信,利用硬件加速功能进行图层合成,并生成最终的显示帧。
-
软件合成:
- 如果选择软件合成,SurfaceFlinger 使用 GPU 或 CPU 进行图层合成,然后将合成后的图像帧发送到显示控制器。
Hardware Composer (hwcomposer)
-
Hardware Composer (hwcomposer) 即硬件合成器,硬件合成器 可以理解为是显示 控制器的一个功能模块。在许多现代显示系统中,它们通常被集成在同一个芯片或模块中,但也可以是分开的组件。无论哪种情况,它们的工作流程是紧密协作的,硬件合成器负责图层合成,显示控制器负责最终的图像输出
-
hwcomposer 也可以是指硬件合成器的硬件抽象层组件,专门负责与显示硬件进行交互。它能够直接利用显示硬件的合成功能,将多个图层高效地合成为一个最终的显示帧。随着 Android 版本的更新,hwcomposer 也经历了多个版本,从早期的 C 接口到后来的 HIDL 接口(如 android.hardware.graphics.composer@2.4)
composer service
- composer service 是一个实现了 HIDL 接口(如 android.hardware.graphics.composer@2.4)的服务,作为 SurfaceFlinger 与 hwcomposer 之间的中间层。
本文主要目的是介绍 Framework 的刷新率设置策略,不关注显示控制器的硬件实现和 composer service 以及 SurfaceFlinger 的实现细节。
(SurfaceFlinger 内部的实现细节以后有时间再专门介绍)
不过要理解 Framework 的刷新率设置策略,有必要先了解 SurfaceFlinger 设置 display mode 和应用刷新率的基本逻辑。
有 3 个参数会影响 SurfaceFlinger 设置 display mode 和应用刷新率
-
base mode(default mode):表示默认的 display mode,即如果没有合适的 display mode,就会选择 base mode
-
primary range:表示屏幕刷新率范围,即目标 display mode 的屏幕刷新率应该在这个范围内
-
app range:表示应用刷新率范围,即目标 display mode 的屏幕刷新率的某个除数应该在这个范围内;将这个除数作为应用刷新率
SurfaceFlinger 定义了一个 display properties 结构,包含这 3 个参数,并且提供了设置 display properties 的接口。
当 Framework 调用接口设置 display porperties 时,SurfaceFlinger 就会根据这几个参数,选择合适的 display mode 和应用刷新率。
Framework 有一套更加复杂的刷新率管理策略,但最终会将这些策略转换成一个 display properties 结构设置到 SurfaceFlinger。
4. Framework 策略
4.1基本架构
Framework 刷新率策略的核心代码包括
-
frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
-
frameworks/base/services/core/java/com/android/server/display/mode/Vote.java
-
frameworks/base/services/core/java/com/android/server/display/mode/VotesStorage.java
Vote(投票、选举)对象表示刷新率的策略。
Vote 包含 1.分辨率(长、宽)2.刷新率范围(包括 "屏幕刷新率范围" 和 "应用刷新率范围") 3.是否禁止动态刷新率 4. base mode 刷新率这几个参数。
VotesStorage 包含一个 Votes map。map 的 key 表示 Vote 的优先级。
一共有 15 个优先级 0~14,值越大优先级越高。
DisplayModeDirector 负责更新 VotesStorage,计算目标 display properties 结构并设置到 SurfaceFlinger。
当设置 "默认刷新率"、"应用刷新率" 时,DisplayModeDirector 更新 VotesStorage,并根据 VotesStorage 中的所有策略计算出目标 base mode、屏幕刷新率范围、应用刷新率范围, 封装到 display properties 结构并设置到 SurfaceFlinger。
下面会在分析刷新率设置流程时,具体介绍 Vote、VotesStorage、DisplayModeDirector 的作用。
4.2 刷新率设置项的定义
在分析刷新率设置流程之前,为了避免混淆,需要先解释各种 xxx 刷新率设置项的定义。
-
最低刷新率:由 system settings "min_refresh_rate" 设置,缺省为 0
-
默认刷新率:优先由 /product/etc/displayconfig/ 或 /vendor/etc/displayconfig/ 下的配置文件设置,其次由 xml 中的 R.integer.config_defaultRefreshRate 设置,缺省为 60
-
默认的用户设置刷新率:优先由 device_config <"display_manager", "peak_refresh_rate_default"> 设置,其次由 /product/etc/displayconfig/ 或 /vendor/etc/displayconfig/ 下的配置文件设置,最次由 xml 中的 R.integer.config_defaultPeakRefreshRate 设置,缺省为 0
-
用户设置的刷新率:由 system settings "peak_refresh_rate" 设置,缺省为 "默认的用户设置刷新率"
-
应用刷新率:由应用窗口属性设置。
-
应用期望的 base mode 刷新率:由应用窗口属性设置。
以上这些设置项实际上都是对 "应用刷新率"(而非 "屏幕刷新率")的限制。
这些 xxx 刷新率设置项会影响不同的 Votes 策略。
0 ---默认策略 ,限制应用刷新率在 [0,默认刷新率] 之间变化
3 --- 用户(user)设置的最低策 略,限制应用刷新率的最低值,即应用刷新率在 [最低刷新率,正无限大] 之间变化
4 --- 应用( app )设置的策略 ,限制应用刷新率范围在 [应用刷新率min ,应用刷新率max] 之间变化
7 --- 用户(user)设置的策略 ,限制应用刷新率在 [0, max(用户设置的刷新率, 最低刷新率)] 之间变化
4.2.1 最低刷新率
由 system settings "min_refresh_rate" 设置,缺省为 0。
略。
4.2.2 默认刷新率 & 默认的用户设置刷新率
4.2.2.1 设置入口
默认刷新率,对应变量 DisplayModeDirector$SettingsObserver.mDefaultRefreshRate。
默认的用户设置刷新率,对应变量 DisplayModeDirector$SettingsObserver.mDefaultPeakRefreshRate。
设置默认刷新率(mDefaultRefreshRate 变量)和默认的用户设置刷新率(mDefaultPeakRefreshRate 变量)的方法都是 SettingsObserver.setRefreshRates。
SettingsObserver.setRefreshRates 方法设置默认刷新率,以及默认的用户设置刷新率。
-
入参 DisplayDeviceConfig displayDeviceConfig 表示 /vendor/etc/displayconfig/ 以及 /product/etc/displayconfig/ 的显示配置;
-
入参 boolean attemptLoadingFromDeviceConfig 表示是否使用 device_config 设置 "默认的用户设置刷新率"
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
1067 @VisibleForTesting
1068 final class SettingsObserver extends ContentObserver {
...
1092
1093 /**
1094 * This is used to update the refresh rate configs from the DeviceConfig, which
1095 * if missing from DisplayDeviceConfig, and finally fallback to config.xml.
1096 */
1097 public void setRefreshRates(DisplayDeviceConfig displayDeviceConfig,
1098 boolean attemptLoadingFromDeviceConfig) {
// 设置 "默认的用户设置刷新率"
1099 setDefaultPeakRefreshRate(displayDeviceConfig, attemptLoadingFromDeviceConfig);
// 设置 "默认刷新率"
1100 mDefaultRefreshRate =
1101 (displayDeviceConfig == null) ? (float) mContext.getResources().getInteger(
1102 R.integer.config_defaultRefreshRate)
1103 : (float) displayDeviceConfig.getDefaultRefreshRate();
1104 }
1105 ...
- 设置 "默认刷新率"
入参 DisplayDeviceConfig displayDeviceConfig 不为 null 时,使用 displayDeviceConfig 的配置来设置 "默认刷新率";
否则,还是使用 R.integer.config_defaultRefreshRate 来设置 "默认刷新率"。
- 设置 "默认的用户设置刷新率"
setDefaultPeakRefreshRate 方法,设置 "默认的用户设置刷新率"。
入参 boolean attemptLoadingFromDeviceConfig 为 true 时,将 "默认的用户设置刷新率" 设置为 device_config <"display_manager", "peak_refresh_rate_default"> 的值;
否则,如果入参 DisplayDeviceConfig displayDeviceConfig 不为 null,则使用 displayDeviceConfig 的配置来设置 "默认的用户设置刷新率";
否则,还是使用 R.integer.config_defaultPeakRefreshRate 来设置 "默认的用户设置刷新率"。
或 DisplayDeviceConfig 更新 mDefaultPeakRefreshRate。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
1173 private void setDefaultPeakRefreshRate(DisplayDeviceConfig displayDeviceConfig,
1174 boolean attemptLoadingFromDeviceConfig) {
1175 Float defaultPeakRefreshRate = null;
1176
// 使用 device_config <"display_manager", "peak_refresh_rate_default"> 的值设置 defaultPeakRefreshRate
1177 if (attemptLoadingFromDeviceConfig) {
1178 try {
1179 defaultPeakRefreshRate =
1180 mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
1181 } catch (Exception exception) {
1182 // Do nothing
1183 }
1184 }
// 优先使用 displayconfig 配置文件的值设置 defaultPeakRefreshRate,
// 其次使用 config.xml 的配置设置 defaultPeakRefreshRate
1185 if (defaultPeakRefreshRate == null) {
1186 defaultPeakRefreshRate =
1187 (displayDeviceConfig == null) ? (float) mContext.getResources().getInteger(
1188 R.integer.config_defaultPeakRefreshRate)
1189 : (float) displayDeviceConfig.getDefaultPeakRefreshRate();
1190 }
1191 mDefaultPeakRefreshRate = defaultPeakRefreshRate;
1192 }
4.2.2.2 设置场景
调用 SettingsObserver.setRefreshRates 的场景有两处。
- 其一是设备启动时,构造 SettingsObserver 对象。
此时,调用 SettingsObserver.setRefreshRates 方法, 由于入参 DisplayDeviceConfig displayDeviceConfig 传入的是 null,入参 boolean attemptLoadingFromDeviceConfig 传入的是 false,因此将
设置 mDefaultRefreshRate 的值为 R.integer.config_defaultRefreshRate;
设置 mDefaultPeakRefreshRate 的值为R.integer.config_defaultPeakRefreshRate。
从注释来看,之所以在 SettingsObserver 对象构造时不用 DeviceConfig 设置 mDefaultRefreshRate 的值,是出于性能考虑。
即读 DeviceConfig 会产生密集的 IO 操作,导致 DMS 启动变慢。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
1067 @VisibleForTesting
1068 final class SettingsObserver extends ContentObserver {
1069 private final Uri mPeakRefreshRateSetting =
1070 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
1071 private final Uri mMinRefreshRateSetting =
1072 Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE);
1073 private final Uri mLowPowerModeSetting =
1074 Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
1075 private final Uri mMatchContentFrameRateSetting =
1076 Settings.Secure.getUriFor(Settings.Secure.MATCH_CONTENT_FRAME_RATE);
1077
1078 private final Context mContext;
1079 private float mDefaultPeakRefreshRate;
1080 private float mDefaultRefreshRate;
1081
1082 SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
1083 super(handler);
1084 mContext = context;
1085 // We don't want to load from the DeviceConfig while constructing since this leads to
1086 // a spike in the latency of DisplayManagerService startup. This happens because
1087 // reading from the DeviceConfig is an intensive IO operation and having it in the
1088 // startup phase where we thrive to keep the latency very low has significant impact.
1089 setRefreshRates(/* displayDeviceConfig= */ null,
1090 /* attemptLoadingFromDeviceConfig= */ false);
1091 }
...
-
其二是主显示设备(主屏幕)准备好(添加到 DMS)时。此时再次调用 SettingsObserver.setRefreshRates 方法,通过传入的 DisplayDeviceConfig 更新 mDefaultRefreshRate 和 mDefaultPeakRefreshRate 的值,入参 boolean attemptLoadingFromDeviceConfig 传入 true。
-
Display.DEFAULT_DISPLAY(0)表示主显示设备
-
DisplayModeDirector.defaultDisplayDeviceUpdated 调用 SettingsObserver.setRefreshRates
-
java
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
3265 private final class LogicalDisplayListener implements LogicalDisplayMapper.Listener {
3266 @Override
3267 public void onLogicalDisplayEventLocked(LogicalDisplay display, int event) {
3268 switch (event) {
3269 case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED:
3270 handleLogicalDisplayAddedLocked(display);
3271 break;
... ...
1783 private void handleLogicalDisplayAddedLocked(LogicalDisplay display) {
1784 final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
1785 final int displayId = display.getDisplayIdLocked();
1786 final boolean isDefault = displayId == Display.DEFAULT_DISPLAY;
1787 configureColorModeLocked(display, device);
1788 if (!mAreUserDisabledHdrTypesAllowed) {
1789 display.setUserDisabledHdrTypes(mUserDisabledHdrTypes);
1790 }
// 注意:只有主显示设备(主屏幕)添加时才会更新 mDefaultRefreshRate 和 mDefaultPeakRefreshRate。
// (1)
1791 if (isDefault) {
1792 notifyDefaultDisplayDeviceUpdated(display);
1793 recordStableDisplayStatsIfNeededLocked(display);
1794 recordTopInsetLocked(display);
1795 }
... ...
1946 private void notifyDefaultDisplayDeviceUpdated(LogicalDisplay display) {
// (2)
1947 mDisplayModeDirector.defaultDisplayDeviceUpdated(display.getPrimaryDisplayDeviceLocked()
1948 .mDisplayDeviceConfig);
1949 }
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
671 /**
672 * Called when the underlying display device of the default display is changed.
673 * Some data in this class relates to the physical display of the device, and so we need to
674 * reload the configurations based on this.
675 * E.g. the brightness sensors and refresh rate capabilities depend on the physical display
676 * device that is being used, so will be reloaded.
677 *
678 * @param displayDeviceConfig configurations relating to the underlying display device.
679 */
680 public void defaultDisplayDeviceUpdated(DisplayDeviceConfig displayDeviceConfig) {
681 synchronized (mLock) {
682 mDefaultDisplayDeviceConfig = displayDeviceConfig;
// (3)
683 mSettingsObserver.setRefreshRates(displayDeviceConfig,
684 /* attemptLoadingFromDeviceConfig= */ true);
685 mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig,
686 /* attemptLoadingFromDeviceConfig= */ true);
687 mBrightnessObserver.reloadLightSensor(displayDeviceConfig);
688 mHbmObserver.setupHdrRefreshRates(displayDeviceConfig);
689 }
690 }
4.2.2.3 DisplayDeviceConfig
首先要注意,DisplayDeviceConfig 不是 DeviceConfig,跟 DeviceConfig 完全没有关系。
DisplayDeviceConfig 即 DisplayDevice 的 Config!
DisplayDeviceConfig 由 DisplayDevice 创建。
调用 DisplayDevice.getDisplayDeviceConfig 方法时,如果 DisplayDeviceConfig 对象(DisplayDeviceConfig.mDisplayDeviceConfig)未创建,则会创建 DisplayDeviceConfig 实例。
DisplayDevice 创建 DisplayDeviceConfig 实例调用的是 DisplayDeviceConfig.create(mContext, false) 方法。
java
// frameworks/base/services/core/java/com/android/server/display/DisplayDevice.java
86 /*
87 * Gets the DisplayDeviceConfig for this DisplayDevice.
88 *
89 * @return The DisplayDeviceConfig; {@code null} if not overridden.
90 */
91 public DisplayDeviceConfig getDisplayDeviceConfig() {
92 if (mDisplayDeviceConfig == null) {
93 mDisplayDeviceConfig = loadDisplayDeviceConfig();
94 }
95 return mDisplayDeviceConfig;
96 }
97
...
391 private DisplayDeviceConfig loadDisplayDeviceConfig() {
392 return DisplayDeviceConfig.create(mContext, false);
393 }
但是 DisplayDevice 是抽象类,其实现类 LocalDisplayDevice 重写了 getDisplayDeviceConfig 方法。
LocalDisplayDevice 创建 DisplayDeviceConfig 实例调用的是 **DisplayDeviceConfig.create(context, mPhysicalDisplayId, mIsFirstDisplay)**方法。
java
// frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java
191 private final class LocalDisplayDevice extends DisplayDevice {
...
473 @Override
474 public DisplayDeviceConfig getDisplayDeviceConfig() {
475 if (mDisplayDeviceConfig == null) {
476 loadDisplayDeviceConfig();
477 }
478 return mDisplayDeviceConfig;
479 }
...
496 private void loadDisplayDeviceConfig() {
497 // Load display device config
498 final Context context = getOverlayContext();
499 mDisplayDeviceConfig = DisplayDeviceConfig.create(context, mPhysicalDisplayId,
500 mIsFirstDisplay);
501
502 // Load brightness HWC quirk
503 mBacklightAdapter.setForceSurfaceControl(mDisplayDeviceConfig.hasQuirk(
504 DisplayDeviceConfig.QUIRK_CAN_SET_BRIGHTNESS_VIA_HWC));
505 }
DisplayDeviceConfig 有 3 种构造方式。
其一是调用 DisplayDeviceConfig.create(Context context, boolean useConfigXml),入参 useConfigXml 传 true,
其二还是调用 DisplayDeviceConfig.create(Context context, boolean useConfigXml),但入参 useConfigXml 传 false。
-
入参 useConfigXml 为 true 时,调用 getConfigFromGlobalXml 方法。
-
入参 useConfigXml 为 false 时,调用 getConfigFromPmValues 方法。
java
// frameworks/base/services/core/java/com/android/server/display/DisplayDeviceConfig.java
725 /**
726 * Creates an instance using global values since no display device config xml exists. Uses
727 * values from config or PowerManager.
728 *
729 * @param context The context from which the DisplayDeviceConfig is to be constructed.
730 * @param useConfigXml A flag indicating if values are to be loaded from the configuration file,
731 * or the default values.
732 * @return A configuration instance.
733 */
734 public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
735 final DisplayDeviceConfig config;
736 if (useConfigXml) {
737 config = getConfigFromGlobalXml(context);
738 } else {
739 config = getConfigFromPmValues(context);
740 }
741 return config;
742 }
useConfigXml 为 true 时。
getConfigFromGlobalXml 方法用 config.xml 中的配置来初始化 DisplayDeviceConfig 属性,包括 "默认刷新率" 和 "默认的用户设置刷新率"。
此时,
默认刷新率(即 mDefaultRefreshRate)由R.integer.config_defaultRefreshRate设置;
默认的用户设置刷新率(即 mDefaultPeakRefreshRate)由R.integer.config_defaultPeakRefreshRate设置。
java
// frameworks/base/services/core/java/com/android/server/display/DisplayDeviceConfig.java
1645 private static DisplayDeviceConfig getConfigFromGlobalXml(Context context) {
1646 DisplayDeviceConfig config = new DisplayDeviceConfig(context);
1647 config.initFromGlobalXml(); // (1)
1648 return config;
1649 }
java
// frameworks/base/services/core/java/com/android/server/display/DisplayDeviceConfig.java
1701 private void initFromGlobalXml() {
1702 // If no ddc exists, use config.xml
1703 loadBrightnessDefaultFromConfigXml();
1704 loadBrightnessConstraintsFromConfigXml();
1705 loadBrightnessMapFromConfigXml();
1706 loadBrightnessRampsFromConfigXml();
1707 loadAmbientLightSensorFromConfigXml();
1708 loadBrightnessChangeThresholdsFromXml();
1709 setProxSensorUnspecified();
1710 loadAutoBrightnessConfigsFromConfigXml();
1711 loadAutoBrightnessAvailableFromConfigXml();
1712 loadRefreshRateSetting(null); // (2)
1713 mLoadedFrom = "<config.xml>";
1714 }
java
// frameworks/base/services/core/java/com/android/server/display/DisplayDeviceConfig.java
1990 private void loadRefreshRateSetting(DisplayConfiguration config) {
1991 final RefreshRateConfigs refreshRateConfigs =
1992 (config == null) ? null : config.getRefreshRate();
1993 BlockingZoneConfig lowerBlockingZoneConfig =
1994 (refreshRateConfigs == null) ? null
1995 : refreshRateConfigs.getLowerBlockingZoneConfigs();
1996 BlockingZoneConfig higherBlockingZoneConfig =
1997 (refreshRateConfigs == null) ? null
1998 : refreshRateConfigs.getHigherBlockingZoneConfigs();
1999 loadPeakDefaultRefreshRate(refreshRateConfigs); // (3)
2000 loadDefaultRefreshRate(refreshRateConfigs); // (4)
2001 loadDefaultRefreshRateInHbm(refreshRateConfigs);
2002 loadLowerRefreshRateBlockingZones(lowerBlockingZoneConfig);
2003 loadHigherRefreshRateBlockingZones(higherBlockingZoneConfig);
2004 loadRefreshRateZoneProfiles(refreshRateConfigs);
2005 }
2006
2007 private void loadPeakDefaultRefreshRate(RefreshRateConfigs refreshRateConfigs) {
2008 if (refreshRateConfigs == null || refreshRateConfigs.getDefaultPeakRefreshRate() == null) {
2009 mDefaultPeakRefreshRate = mContext.getResources().getInteger(
2010 R.integer.config_defaultPeakRefreshRate);
2011 } else {
2012 mDefaultPeakRefreshRate =
2013 refreshRateConfigs.getDefaultPeakRefreshRate().intValue();
2014 }
2015 }
2016
2017 private void loadDefaultRefreshRate(RefreshRateConfigs refreshRateConfigs) {
2018 if (refreshRateConfigs == null || refreshRateConfigs.getDefaultRefreshRate() == null) {
2019 mDefaultRefreshRate = mContext.getResources().getInteger(
2020 R.integer.config_defaultRefreshRate);
2021 } else {
2022 mDefaultRefreshRate =
2023 refreshRateConfigs.getDefaultRefreshRate().intValue();
2024 }
2025 }
...
useConfigXml 为 false 时。
getConfigFromPmValues 方法用 PowerManager 中写死的常量值来初始化 DisplayDeviceConfig 属性,主要初始化了一些背光相关的属性,没有设置默认刷新率(即 mDefaultRefreshRate)和默认的用户设置刷新率(即 mDefaultPeakRefreshRate)。
java
// frameworks/base/services/core/java/com/android/server/display/DisplayDeviceConfig.java
1651 private static DisplayDeviceConfig getConfigFromPmValues(Context context) {
1652 DisplayDeviceConfig config = new DisplayDeviceConfig(context);
1653 config.initFromDefaultValues(); // (1)
1654 return config;
1655 }
java
// frameworks/base/services/core/java/com/android/server/display/DisplayDeviceConfig.java
1716 private void initFromDefaultValues() {
1717 // Set all to basic values
1718 mLoadedFrom = "Static values";
1719 mBacklightMinimum = PowerManager.BRIGHTNESS_MIN;
1720 mBacklightMaximum = PowerManager.BRIGHTNESS_MAX;
1721 mBrightnessDefault = BRIGHTNESS_DEFAULT;
1722 mBrightnessRampFastDecrease = PowerManager.BRIGHTNESS_MAX;
1723 mBrightnessRampFastIncrease = PowerManager.BRIGHTNESS_MAX;
1724 mBrightnessRampSlowDecrease = PowerManager.BRIGHTNESS_MAX;
1725 mBrightnessRampSlowIncrease = PowerManager.BRIGHTNESS_MAX;
1726 mBrightnessRampDecreaseMaxMillis = 0;
1727 mBrightnessRampIncreaseMaxMillis = 0;
1728 setSimpleMappingStrategyValues();
1729 loadAmbientLightSensorFromConfigXml();
1730 setProxSensorUnspecified();
1731 loadAutoBrightnessAvailableFromConfigXml();
1732 }
其三 是调用 DisplayDeviceConfig.create(Context context, long physicalDisplayId, boolean isFirstDisplay) 方法(*)。
系统中的 DisplayDeviceConfig 实例默认是通过这种方式构造的(参见上文中 LocalDisplayDevice 的 getDisplayDeviceConfig 方法),因此我们需要重点关心。
java
// frameworks/base/services/core/java/com/android/server/display/DisplayDeviceConfig.java
704 /**
705 * Creates an instance for the specified display. Tries to find a file with identifier in the
706 * following priority order:
707 * <ol>
708 * <li>physicalDisplayId</li>
709 * <li>physicalDisplayId without a stable flag (old system)</li>
710 * <li>portId</li>
711 * </ol>
712 *
713 * @param physicalDisplayId The display ID for which to load the configuration.
714 * @return A configuration instance for the specified display.
715 */
716 public static DisplayDeviceConfig create(Context context, long physicalDisplayId,
717 boolean isFirstDisplay) {
// (1)
718 final DisplayDeviceConfig config = createWithoutDefaultValues(context, physicalDisplayId,
719 isFirstDisplay);
720
721 config.copyUninitializedValuesFromSecondaryConfig(loadDefaultConfigurationXml(context));
722 return config;
723 }
createWithoutDefaultValues 方法创建 DisplayDeviceConfig 实例,解析相关的配置文件,初始化 DisplayDeviceConfig 的属性。
(copyUninitializedValuesFromSecondaryConfig 方法与刷新率没什么关系,这里可以忽略。)
- 配置文件所在目录的优先级
优先加载 product 目录下的配置文件(/product/etc/displayconfig);
product 目录下没有配置文件时,再加载 vendor 目录下的配置文件(/vendor/etc/displayconfig)。
- 配置文件的优先级
优先加载目标屏幕 id 对应的配置,即 display_id_%d.xml 文件,%d 表示 id 值;
其次加载目标屏幕 id 对应的配置,即 display_%d.xml 文件,%d 表示不带 stable flag 的 id 值;
最次加载目标屏幕端口号(port)对应的配置,即 display_port_%d.xml 文件,%d 表示端口号。
java
// frameworks/base/services/core/java/com/android/server/display/DisplayDeviceConfig.java
744 private static DisplayDeviceConfig createWithoutDefaultValues(Context context,
745 long physicalDisplayId, boolean isFirstDisplay) {
746 DisplayDeviceConfig config;
747
// 加载 product 目录下的配置
748 config = loadConfigFromDirectory(context, Environment.getProductDirectory(),
749 physicalDisplayId);
750 if (config != null) {
751 return config;
752 }
753
// 加载 vendor 目录下的配置
754 config = loadConfigFromDirectory(context, Environment.getVendorDirectory(),
755 physicalDisplayId);
756 if (config != null) {
757 return config;
758 }
759
760 // If no config can be loaded from any ddc xml at all,
761 // prepare a whole config using the global config.xml.
762 // Guaranteed not null
763 return create(context, isFirstDisplay);
764 }
java
444 private static final String ETC_DIR = "etc";
445 private static final String DISPLAY_CONFIG_DIR = "displayconfig";
446 private static final String CONFIG_FILE_FORMAT = "display_%s.xml";
447 private static final String DEFAULT_CONFIG_FILE = "default.xml";
448 private static final String DEFAULT_CONFIG_FILE_WITH_UIMODE_FORMAT = "default_%s.xml";
449 private static final String PORT_SUFFIX_FORMAT = "port_%d";
450 private static final String STABLE_ID_SUFFIX_FORMAT = "id_%d";
451 private static final String NO_SUFFIX_FORMAT = "%d";
452 private static final long STABLE_FLAG = 1L << 62;
...
815 private static DisplayDeviceConfig loadConfigFromDirectory(Context context,
816 File baseDirectory, long physicalDisplayId) {
817 DisplayDeviceConfig config;
// 加载目标屏幕 id 对应的配置,即 display_id_%d.xml 文件,%d 表示 id 值
818 // Create config using filename from physical ID (including "stable" bit).
819 config = getConfigFromSuffix(context, baseDirectory, STABLE_ID_SUFFIX_FORMAT,
820 physicalDisplayId);
821 if (config != null) {
822 return config;
823 }
824
// 加载目标屏幕 id 对应的配置,即 display_%d.xml 文件,%d 表示不带 stable flag 的 id 值。
// Android 用 id 的高 63 位(从 0 开始第 62 位)表示屏幕 id 是否固定,即 stable flag。
825 // Create config using filename from physical ID (excluding "stable" bit).
826 final long withoutStableFlag = physicalDisplayId & ~STABLE_FLAG;
827 config = getConfigFromSuffix(context, baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag);
828 if (config != null) {
829 return config;
830 }
831
// 加载目标屏幕端口号(port)对应的配置,即 display_port_%d.xml 文件,%d 表示端口号
832 // Create config using filename from port ID.
833 final DisplayAddress.Physical physicalAddress =
834 DisplayAddress.fromPhysicalDisplayId(physicalDisplayId);
835 int port = physicalAddress.getPort();
836 config = getConfigFromSuffix(context, baseDirectory, PORT_SUFFIX_FORMAT, port);
837 return config;
838 }
java
1631 private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
1632 String suffixFormat, long idNumber) {
1633
1634 final String suffix = String.format(Locale.ROOT, suffixFormat, idNumber);
1635 final String filename = String.format(Locale.ROOT, CONFIG_FILE_FORMAT, suffix);
1636 final File filePath = Environment.buildPath(
1637 baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename);
1638 final DisplayDeviceConfig config = new DisplayDeviceConfig(context);
// 读取配置文件,初始化 DisplayDeviceConfig 属性
1639 if (config.initFromFile(filePath)) {
1640 return config;
1641 }
1642 return null;
1643 }
java
1657 @VisibleForTesting
1658 boolean initFromFile(File configFile) {
1659 if (!configFile.exists()) {
1660 // Display configuration files aren't required to exist.
1661 return false;
1662 }
1663
1664 if (!configFile.isFile()) {
1665 Slog.e(TAG, "Display configuration is not a file: " + configFile + ", skipping");
1666 return false;
1667 }
1668
1669 try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
1670 final DisplayConfiguration config = XmlParser.read(in);
1671 if (config != null) {
1672 loadName(config);
1673 loadDensityMapping(config);
1674 loadBrightnessDefaultFromDdcXml(config);
1675 loadBrightnessConstraintsFromConfigXml();
1676 loadBrightnessMap(config);
1677 loadThermalThrottlingConfig(config);
1678 loadHighBrightnessModeData(config);
1679 loadQuirks(config);
1680 loadBrightnessRamps(config);
1681 loadAmbientLightSensorFromDdc(config);
1682 loadScreenOffBrightnessSensorFromDdc(config);
1683 loadProxSensorFromDdc(config);
1684 loadAmbientHorizonFromDdc(config);
1685 loadBrightnessChangeThresholds(config);
1686 loadAutoBrightnessConfigValues(config);
// 初始化刷新率相关的配置
1687 loadRefreshRateSetting(config);
1688 loadScreenOffBrightnessSensorValueToLuxFromDdc(config);
1689 loadUsiVersion(config);
1690 } else {
1691 Slog.w(TAG, "DisplayDeviceConfig file is null");
1692 }
1693 } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
1694 Slog.e(TAG, "Encountered an error while reading/parsing display config file: "
1695 + configFile, e);
1696 }
1697 mLoadedFrom = configFile.toString();
1698 return true;
1699 }
这里又回到了 loadRefreshRateSetting 方法。
如果配置文件中包含了默认刷新率、默认的用户设置刷新率配置,则将默认刷新率(即 mDefaultRefreshRate )、默认的用户设置刷新率(即 mDefaultPeakRefreshRate)设置为配置文件中的值。
否则,使用 config.xml 中的配置,即 mDefaultRefreshRate 由 R.integer.config_defaultRefreshRate 设置, mDefaultPeakRefreshRate 由 R.integer.config_defaultPeakRefreshRate 设置。
如果配置文件和 config.xml(R.integer.config_defaultRefreshRate、R.integer.config_defaultPeakRefreshRate)都没有配置默认刷新率、默认的用户设置刷新率,则使用缺省值。
java
// frameworks/base/services/core/java/com/android/server/display/DisplayDeviceConfig.java
453 private static final int DEFAULT_PEAK_REFRESH_RATE = 0;
454 private static final int DEFAULT_REFRESH_RATE = 60;
...
622 /**
623 * The default peak refresh rate for a given device. This value prevents the framework from
624 * using higher refresh rates, even if display modes with higher refresh rates are available
625 * from hardware composer. Only has an effect if the value is non-zero.
626 */
627 private int mDefaultPeakRefreshRate = DEFAULT_PEAK_REFRESH_RATE;
628
629 /**
630 * The default refresh rate for a given device. This value sets the higher default
631 * refresh rate. If the hardware composer on the device supports display modes with
632 * a higher refresh rate than the default value specified here, the framework may use those
633 * higher refresh rate modes if an app chooses one by setting preferredDisplayModeId or calling
634 * setFrameRate(). We have historically allowed fallback to mDefaultPeakRefreshRate if
635 * mDefaultRefreshRate is set to 0, but this is not supported anymore.
636 */
637 private int mDefaultRefreshRate = DEFAULT_REFRESH_RATE;
本地 Android 设备没有 /<product | vendor>/etc/displayconfig。
bash
yudi:/product # find -name "displayconfig"
yudi:/vendor # find -name "displayconfig"
"默认的用户设置刷新率" 可以通过 device_config <"display_manager", "peak_refresh_rate_default"> 设置,因此也可以通过手动设置 device_config <"display_manager", "peak_refresh_rate_default"> 来调用 "刷新率更新和应用流程"。
4.2.3 用户设置的刷新率
由 system settings "peak_refresh_rate" 设置。
即可以通过 adb shell settings put system peak_refresh_rate <value> 设置刷新率。
"用户设置的刷新率" 在 Framework 中没有对应的全局变量,设置 "用户设置的刷新率" 会调用 "刷新率更新和应用流程"。
4.2.4 应用刷新率 & 应用期望的 base mode 刷新率
应用刷新率 & 应用期望的 base mode 刷新率都是通过应用的窗口属性来设置的。
4.3 刷新率更新 & 应用流程
4.3.1 入口
刷新率更新 & 应用的主要入口包括
-
当 "默认的用户设置刷新率" 或 "用户设置的刷新率" 变化时~
-
"默认的用户设置刷新率" 可以通过 device_config <"display_manager", "peak_refresh_rate_default"> 修改
-
"用户设置的刷新率" 可以通过 system settings "peak_refresh_rate" 修改
-
-
窗口切换,且上层的窗口设置了 "期望的 base mode id"、"期望的 base mode 刷新率" 或 "应用刷新率范围" 属性时~
4.3.1.1 system settings "peak_refresh_rate"
DisplayModeDirector 初始化时,开始监听 system settings "peak_refresh_rate" 变化。
监听到 system settings "peak_refresh_rate" 变化时,调用 updateRefreshRateSettingLocked 方法更新(应用)刷新率。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
152 public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
153 @NonNull Injector injector) {
...
// (1)
162 mSettingsObserver = new SettingsObserver(context, handler);
...
174 }
176 /**
177 * Tells the DisplayModeDirector to update allowed votes and begin observing relevant system
178 * state.
179 *
180 * This has to be deferred because the object may be constructed before the rest of the system
181 * is ready.
182 */
183 public void start(SensorManager sensorManager) {
// (2)
184 mSettingsObserver.observe();
185 mDisplayObserver.observe();
186 mBrightnessObserver.observe(sensorManager);
187 mSensorObserver.observe();
188 mHbmObserver.observe();
189 mSkinThermalStatusObserver.observe();
190 synchronized (mLock) {
191 // We may have a listener already registered before the call to start, so go ahead and
192 // notify them to pick up our newly initialized state.
193 notifyDesiredDisplayModeSpecsChangedLocked();
194 }
195 }
1067 @VisibleForTesting
1068 final class SettingsObserver extends ContentObserver {
1069 private final Uri mPeakRefreshRateSetting =
1070 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
...
1106 public void observe() {
1107 final ContentResolver cr = mContext.getContentResolver();
// (3)
1108 mInjector.registerPeakRefreshRateObserver(cr, this);
1109 cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
1110 UserHandle.USER_SYSTEM);
1111 cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
1112 UserHandle.USER_SYSTEM);
1113 cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
1114 this);
1115
1116 Float deviceConfigDefaultPeakRefresh =
1117 mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
1118 if (deviceConfigDefaultPeakRefresh != null) {
1119 mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh;
1120 }
1121
1122 synchronized (mLock) {
1123 updateRefreshRateSettingLocked();
1124 updateLowPowerModeSettingLocked();
1125 updateModeSwitchingTypeSettingLocked();
1126 }
1127 }
...
1207 private void updateRefreshRateSettingLocked() {
1208 final ContentResolver cr = mContext.getContentResolver();
1209 float minRefreshRate = Settings.System.getFloatForUser(cr,
1210 Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
1211 float peakRefreshRate = Settings.System.getFloatForUser(cr,
1212 Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId());
// (6)
1213 updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
1214 }
...
1149 @Override
1150 public void onChange(boolean selfChange, Uri uri, int userId) {
1151 synchronized (mLock) {
// (5)
1152 if (mPeakRefreshRateSetting.equals(uri)
1153 || mMinRefreshRateSetting.equals(uri)) {
1154 updateRefreshRateSettingLocked();
1155 } else if (mLowPowerModeSetting.equals(uri)) {
1156 updateLowPowerModeSettingLocked();
1157 } else if (mMatchContentFrameRateSetting.equals(uri)) {
1158 updateModeSwitchingTypeSettingLocked();
1159 }
1160 }
1161 }
...
2876 @VisibleForTesting
2877 static class RealInjector implements Injector {
2847 Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
...
2890
2891 @Override
2892 public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
2893 @NonNull ContentObserver observer) {
// (4)
2894 cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
2895 observer, UserHandle.USER_SYSTEM);
2896 }
...
4.3.1.2 device_config <"display_manager", "peak_refresh_rate_default">
DisplayModeDirector 初始化时,创建 DeviceConfigDisplaySettings 对象开始监听 device_config <"display_manager", "peak_refresh_rate_default"> 变化。
监听到 device_config <"display_manager", "peak_refresh_rate_default"> 变化时,
通过 msg 通知 SettingsObserver 默认刷新率变化,调用 updateRefreshRateSettingLocked 方法更新(应用)刷新率。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
// DeviceConfigDisplaySettings:device_config 监听器
2676 private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
2677 public void startListening() {
...
2762 /*
2763 * Return null if no such property
2764 */
2765 public Float getDefaultPeakRefreshRate() {
// (2)
2766 float defaultPeakRefreshRate = mDeviceConfig.getFloat(
2767 DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
2768 DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
2769
2770 if (defaultPeakRefreshRate == -1) {
2771 return null;
2772 }
2773 return defaultPeakRefreshRate;
2774 }
2775
2776 @Override
2777 public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
// (1)
2778 Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
// (3)
2779 mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
2780 defaultPeakRefreshRate).sendToTarget();
...
// DisplayModeDirectorHandler:DisplayModeDirector 的 Handler
886 private final class DisplayModeDirectorHandler extends Handler {
887 DisplayModeDirectorHandler(Looper looper) {
888 super(looper, null, true /*async*/);
889 }
...
// (4)
924 case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
925 Float defaultPeakRefreshRate = (Float) msg.obj;
926 mSettingsObserver.onDeviceConfigDefaultPeakRefreshRateChanged(
927 defaultPeakRefreshRate);
928 break;
...
// SettingsObserver:settings 监听器
1067 @VisibleForTesting
1068 final class SettingsObserver extends ContentObserver {
...
1136 public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
1137 synchronized (mLock) {
// (5)
1138 if (defaultPeakRefreshRate == null) {
1139 setDefaultPeakRefreshRate(mDefaultDisplayDeviceConfig,
1140 /* attemptLoadingFromDeviceConfig= */ false);
1141 updateRefreshRateSettingLocked();
1142 } else if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) {
1143 mDefaultPeakRefreshRate = defaultPeakRefreshRate;
1144 updateRefreshRateSettingLocked();
1145 }
1146 }
1147 }
4.3.1.3 setDefaultRefreshRate 方法设置 "默认刷新率"(Just for test)
此方法只在 DisplayModeDirector 的单元测试代码中使用。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
1067 @VisibleForTesting
1068 final class SettingsObserver extends ContentObserver {
...
1129 public void setDefaultRefreshRate(float refreshRate) {
1130 synchronized (mLock) {
1131 mDefaultRefreshRate = refreshRate;
1132 updateRefreshRateSettingLocked();
1133 }
1134 }
...
4.3.1.4 窗口切换
窗口切换时,系统调用 DisplayContent.applySurfaceChangesTransaction 方法。
在 DisplayContent.applySurfaceChangesTransaction 方法中
-
遍历所有 windows,更新 mTmpApplySurfaceChangesTransactionState。mTmpApplySurfaceChangesTransactionState 保存目标 base mode id、目标 base mode 的刷新率、"应用刷新率 [min, max]" 信息。
-
调用 DMS.LocalService.setDisplayProperties 方法将 base mode 和 "应用刷新率 [min, max]" 更新到 VotesStorage,VotesStorage 更新时通过 VotesStorage.mListener 回调 DisplayModeDirector.notifyDesiredDisplayModeSpecsChangedLocked 方法将 base mode 和 "应用刷新率 [min, max]" 设置到 SurfaceFlinger。
java
// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
4934 // TODO: Super unexpected long method that should be broken down...
4935 void applySurfaceChangesTransaction() {
4936 final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
4937
4938 beginHoldScreenUpdate();
4939
4940 mTmpUpdateAllDrawn.clear();
4941
4942 if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("On entry to LockedInner",
4943 pendingLayoutChanges);
4944
4945 if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
4946 mWallpaperController.adjustWallpaperWindows();
4947 }
4948
4949 if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
4950 if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
4951 if (updateOrientation()) {
4952 setLayoutNeeded();
4953 sendNewConfiguration();
4954 }
4955 }
4956
4957 if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
4958 setLayoutNeeded();
4959 }
4960
4961 // Perform a layout, if needed.
4962 performLayout(true /* initial */, false /* updateInputWindows */);
4963 pendingLayoutChanges = 0;
4964
4965 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyPostLayoutPolicy");
4966 try {
4967 mDisplayPolicy.beginPostLayoutPolicyLw();
4968 forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
4969 mDisplayPolicy.finishPostLayoutPolicyLw();
4970 } finally {
4971 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
4972 }
4973 mInsetsStateController.onPostLayout();
4974
4975 mTmpApplySurfaceChangesTransactionState.reset();
4976
4977 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
4978 try {
// 遍历所有 windows,对每个 window 调用 mApplySurfaceChangesTransaction 方法,
// 更新 mTmpApplySurfaceChangesTransactionState
4979 forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
4980 } finally {
4981 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
4982 }
4983 prepareSurfaces();
4984
4985 // This should be called after the insets have been dispatched to clients and we have
4986 // committed finish drawing windows.
4987 mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();
4988
4989 mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
4990 if (!inTransition() && !mDisplayRotation.isRotatingSeamlessly()) {
// 设置 display properties
4991 mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
4992 mLastHasContent,
4993 mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
4994 mTmpApplySurfaceChangesTransactionState.preferredModeId,
4995 mTmpApplySurfaceChangesTransactionState.preferredMinRefreshRate,
4996 mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate,
4997 mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
4998 mTmpApplySurfaceChangesTransactionState.disableHdrConversion,
4999 true /* inTraversal, must call performTraversalInTrans... below */);
5000 }
5001 // If the display now has content, or no longer has content, update recording.
5002 updateRecording();
5003
5004 final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
5005 if (wallpaperVisible != mLastWallpaperVisible) {
5006 mLastWallpaperVisible = wallpaperVisible;
5007 mWmService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this);
5008 }
5009
5010 while (!mTmpUpdateAllDrawn.isEmpty()) {
5011 final ActivityRecord activity = mTmpUpdateAllDrawn.removeLast();
5012 // See if any windows have been drawn, so they (and others associated with them)
5013 // can now be shown.
5014 activity.updateAllDrawn();
5015 }
5016
5017 finishHoldScreenUpdate();
5018 }
DMS.LocalService.setDisplayProperties 方法。(VotesStorage 以及 VotesStorage.mListener 回调会在更后面分析)
-> DMS.setDisplayPropertiesInternal
-> DisplayModeDirector$AppRequestObserver.setAppRequest
java
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
4255 @VisibleForTesting
4256 final class LocalService extends DisplayManagerInternal {
4257
...
4409 @Override
4410 public void setDisplayProperties(int displayId, boolean hasContent,
4411 float requestedRefreshRate, int requestedMode, float requestedMinRefreshRate,
4412 float requestedMaxRefreshRate, boolean requestedMinimalPostProcessing,
4413 boolean disableHdrConversion, boolean inTraversal) {
// (1)
4414 setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate,
4415 requestedMode, requestedMinRefreshRate, requestedMaxRefreshRate,
4416 requestedMinimalPostProcessing, disableHdrConversion, inTraversal);
4417 }
...
java
2431 void setDisplayPropertiesInternal(int displayId, boolean hasContent,
2432 float requestedRefreshRate, int requestedModeId, float requestedMinRefreshRate,
2433 float requestedMaxRefreshRate, boolean preferMinimalPostProcessing,
2434 boolean disableHdrConversion, boolean inTraversal) {
2435 synchronized (mSyncRoot) {
2436 final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
2437 if (display == null) {
2438 return;
2439 }
2440
2441 boolean shouldScheduleTraversal = false;
2442
2443 if (display.hasContentLocked() != hasContent) {
2444 if (DEBUG) {
2445 Slog.d(TAG, "Display " + displayId + " hasContent flag changed: "
2446 + "hasContent=" + hasContent + ", inTraversal=" + inTraversal);
2447 }
2448
2449 display.setHasContentLocked(hasContent);
2450 shouldScheduleTraversal = true;
2451 }
// (2)
// 入参 requestedModeId 表示应用期望的 base mode id,
// 入参 requestedRefreshRate 表示应用期望的 base mode 的刷新率,
// 如果入参 requestedModeId 有效(即不为 0),则将 requestedModeId 设为 base mode,
// 如果入参 requestedModeId 无效(即为 0),且入参 requestedRefreshRate 有效,
// 则找到与 requestedRefreshRate 匹配的 display mode,并设为 base mode。
2452 if (requestedModeId == 0 && requestedRefreshRate != 0) {
2453 // Scan supported modes returned by display.getInfo() to find a mode with the same
2454 // size as the default display mode but with the specified refresh rate instead.
2455 Display.Mode mode = display.getDisplayInfoLocked().findDefaultModeByRefreshRate(
2456 requestedRefreshRate);
2457 if (mode != null) {
2458 requestedModeId = mode.getModeId();
2459 } else {
2460 Slog.e(TAG, "Couldn't find a mode for the requestedRefreshRate: "
2461 + requestedRefreshRate + " on Display: " + displayId);
2462 }
2463 }
// (3)
2464 mDisplayModeDirector.getAppRequestObserver().setAppRequest(
2465 displayId, requestedModeId, requestedMinRefreshRate, requestedMaxRefreshRate);
...
DisplayModeDirector$AppRequestObserver.setAppRequest 方法。
-
调用 setAppRequestedModeLocked 方法更新 "应用期望的 base mode 刷新率"
-
调用 setAppPreferredRefreshRateRangeLocked 方法更新 "应用刷新率" 范围
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
1272 /**
1273 * Responsible for keeping track of app requested refresh rates per display
1274 */
1275 public final class AppRequestObserver {
...
1284 /**
1285 * Sets refresh rates from app request
1286 */
// 设置应用期望的 base mode 刷新率 & 应用刷屏率
1287 public void setAppRequest(int displayId, int modeId, float requestedMinRefreshRateRange,
1288 float requestedMaxRefreshRateRange) {
1289 synchronized (mLock) {
// 设置应用期望的 base mode(刷新率)
1290 setAppRequestedModeLocked(displayId, modeId);
// 设置应用刷新率
1291 setAppPreferredRefreshRateRangeLocked(displayId, requestedMinRefreshRateRange,
1292 requestedMaxRefreshRateRange);
1293 }
1294 }
1295
// 设置应用期望的 base mode 刷新率
1296 private void setAppRequestedModeLocked(int displayId, int modeId) {
// 目标 base mode 和当前的 base mode 相同时,直接返回
1297 final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId);
1298 if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
1299 return;
1300 }
1301
1302 final Vote baseModeRefreshRateVote;
1303 final Vote sizeVote;
// 目标 base mode 有效,则将该 mode 的刷新率作为应用期望的 base mode 刷新率
1304 if (requestedMode != null) {
1305 mAppRequestedModeByDisplay.put(displayId, requestedMode);
1306 baseModeRefreshRateVote =
1307 Vote.forBaseModeRefreshRate(requestedMode.getRefreshRate());
1308 sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
1309 requestedMode.getPhysicalHeight());
// 目标 base mode 无效(没有该 mode)时,清除应用期望的 base mode 刷新率设置
1310 } else {
1311 mAppRequestedModeByDisplay.remove(displayId);
1312 baseModeRefreshRateVote = null;
1313 sizeVote = null;
1314 }
1315
// 更新 VotesStorage
1316 mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
1317 baseModeRefreshRateVote);
1318 mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
1319 }
1320
// 设置应用设置的范围
1321 private void setAppPreferredRefreshRateRangeLocked(int displayId,
1322 float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) {
1323 final Vote vote;
1324
// 检查目标范围是否无效。
// 目标 min 和 max 至少有一个应该 > 0.
// 目标 max <=0 时,目标范围为 [min, 无限大];
// 目标 max > 0 时,目标范围为 [min, max]。
1325 RefreshRateRange refreshRateRange = null;
1326 if (requestedMinRefreshRateRange > 0 || requestedMaxRefreshRateRange > 0) {
1327 float min = requestedMinRefreshRateRange;
1328 float max = requestedMaxRefreshRateRange > 0
1329 ? requestedMaxRefreshRateRange : Float.POSITIVE_INFINITY;
1330 refreshRateRange = new RefreshRateRange(min, max);
1331 if (refreshRateRange.min == 0 && refreshRateRange.max == 0) {
1332 // requestedMinRefreshRateRange/requestedMaxRefreshRateRange were invalid
1333 refreshRateRange = null;
1334 }
1335 }
1336
// 目标范围和当前的 "应用刷新率" 范围相同,直接返回
1337 if (Objects.equals(refreshRateRange,
1338 mAppPreferredRefreshRateRangeByDisplay.get(displayId))) {
1339 return;
1340 }
1341
1342 if (refreshRateRange != null) {
1343 mAppPreferredRefreshRateRangeByDisplay.put(displayId, refreshRateRange);
1344 vote = Vote.forRenderFrameRates(refreshRateRange.min, refreshRateRange.max);
// 目标范围无效时,清除"应用刷新率" 范围设置
1345 } else {
1346 mAppPreferredRefreshRateRangeByDisplay.remove(displayId);
1347 vote = null;
1348 }
// 更新 VotesStorage
1349 mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
1350 vote);
1351 }
遍历窗口,获取窗口 "期望的 base mode id"、"期望的 base mode 刷新率" 或 "应用刷新率范围" 属性的方法是 mApplySurfaceChangesTransaction 。
对应的窗口属性分别是:
WindowManager$LayoutParams.preferredRefreshRate
WindowManager$LayoutParams.preferredDisplayModeId
WindowManager$LayoutParams.preferredMinDisplayRefreshRate
WindowManager$LayoutParams.preferredMaxDisplayRefreshRate
4.3.1.5 小结
-
设置 system settings "peak_refresh_rate" 或 device_config <"display_manager", "peak_refresh_rate_default"> 时("用户设置的刷新率"、"默认的用户设置刷新率" 变化),调用 DisplayModeDirector.updateRefreshRateSettingLocked 更新刷新率;
-
窗口切换,并且可见窗口包含以下属性时
-
调用 mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE, vote) 更新目标屏幕 id 的 ""刷新率策略。
vote 是由这两个窗口属性值计算得到的 "应用( app )设置的策略"
-
WindowManager$LayoutParams.preferredMinDisplayRefreshRate
-
WindowManager$LayoutParams.preferredMaxDisplayRefreshRate
-
-
调用 mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, baseModeRefreshRateVote) 更新目标屏幕 id 的刷新率策略。
baseModeRefreshRateVote 是由这两个窗口属性值计算得到的 "应用( app )设置的 base mode 刷新率策略"
-
WindowManager$LayoutParams.preferredRefreshRate
-
WindowManager$LayoutParams.preferredDisplayModeId
-
-
实际上,DisplayModeDirector.updateRefreshRateSettingLocked 方法其实也是计算得到 "策略对象" 并更新到 VotesStorage。
VotesStorage 更新时再回调用 VotesStorage.mListener 应用更新。
只不过,DisplayModeDirector.updateRefreshRateSettingLocked 是根据 "最低刷新率"、"默认刷新率"、"默认的用户设置刷新率", "用户设置的刷新率" 这些设置计算得到 "用户(user)设置的最低策 略"、"默认策略 "、"用户(user)设置的策略" 的策略对象,并调用 VotesStorage.updateGlobalVote 更新全局刷新率策略。
4.3.2 Vote & VotesStorage
4.3.2.1 Vote
Vote(投票、选举)对象表示刷新率的策略。
一共有 15 种策略,每种策略有一个优先级,从 0~14,值越大策略的优先级越高。
0 ---默认策略 ,限制应用刷新率在 [0,默认刷新率] 之间变化
3 --- 用户(user)设置的最低策 略,限制应用刷新率的最低值,即应用刷新率在 [最低刷新率,正无限大] 之间变化
4 --- 应用( app )设置的策略 ,限制应用刷新率范围在 [应用刷新率min ,应用刷新率max] 之间变化
5 --- 应用( app )设置的 base mode 刷新率策略,应用期望的 base mode 刷新率的值
7 --- 用户(user)设置的策略 ,限制应用刷新率在 [0, max(用户设置的刷新率, 最低刷新率)] 之间变化
本文只介绍这几个策略,这几个策略中除 5 以外都只跟 "应用刷新率" 相关。"屏幕刷新率" 相关的策略主要跟背光、HBM(屏幕亮度增强模式),UDFPS(屏下指纹)相关。
这些策略优先级的定义如下,它们是常量。
java
// frameworks/base/services/core/java/com/android/server/display/mode/Vote.java
21 final class Vote {
22 // DEFAULT_RENDER_FRAME_RATE votes for render frame rate [0, DEFAULT]. As the lowest
23 // priority vote, it's overridden by all other considerations. It acts to set a default
24 // frame rate for a device.
// 默认策略
25 static final int PRIORITY_DEFAULT_RENDER_FRAME_RATE = 0;
26
27 // PRIORITY_FLICKER_REFRESH_RATE votes for a single refresh rate like [60,60], [90,90] or
28 // null. It is used to set a preferred refresh rate value in case the higher priority votes
29 // result is a range.
30 static final int PRIORITY_FLICKER_REFRESH_RATE = 1;
31
32 // High-brightness-mode may need a specific range of refresh-rates to function properly.
33 static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 2;
34
35 // SETTING_MIN_RENDER_FRAME_RATE is used to propose a lower bound of the render frame rate.
36 // It votes [minRefreshRate, Float.POSITIVE_INFINITY]
// 用户(user)设置的最低策略
37 static final int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;
38
39 // APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render
40 // frame rate in certain cases, mostly to preserve power.
41 // @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate
42 // @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate
43 // It votes to [preferredMinRefreshRate, preferredMaxRefreshRate].
// 应用(app)设置的策略
44 static final int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 4;
45
46 // We split the app request into different priorities in case we can satisfy one desire
47 // without the other.
48
49 // Application can specify preferred refresh rate with below attrs.
50 // @see android.view.WindowManager.LayoutParams#preferredRefreshRate
51 // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
52 //
53 // When the app specifies a LayoutParams#preferredDisplayModeId, in addition to the
54 // refresh rate, it also chooses a preferred size (resolution) as part of the selected
55 // mode id. The app preference is then translated to APP_REQUEST_BASE_MODE_REFRESH_RATE and
56 // optionally to APP_REQUEST_SIZE as well, if a mode id was selected.
57 // The system also forces some apps like denylisted app to run at a lower refresh rate.
58 // @see android.R.array#config_highRefreshRateBlacklist
59 //
60 // When summarizing the votes and filtering the allowed display modes, these votes determine
61 // which mode id should be the base mode id to be sent to SurfaceFlinger:
62 // - APP_REQUEST_BASE_MODE_REFRESH_RATE is used to validate the vote summary. If a summary
63 // includes a base mode refresh rate, but it is not in the refresh rate range, then the
64 // summary is considered invalid so we could drop a lower priority vote and try again.
65 // - APP_REQUEST_SIZE is used to filter out display modes of a different size.
66 //
67 // The preferred refresh rate is set on the main surface of the app outside of
68 // DisplayModeDirector.
// 应用(app)设置的 base mode 刷新率策略
69 // @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
70 static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 5;
71 static final int PRIORITY_APP_REQUEST_SIZE = 6;
72
73 // SETTING_PEAK_RENDER_FRAME_RATE has a high priority and will restrict the bounds of the
74 // rest of low priority voters. It votes [0, max(PEAK, MIN)]
// 用户(user)设置的策略
75 static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 7;
76
77 // To avoid delay in switching between 60HZ -> 90HZ when activating LHBM, set refresh
78 // rate to max value (same as for PRIORITY_UDFPS) on lock screen
79 static final int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 8;
80
81 // For concurrent displays we want to limit refresh rate on all displays
82 static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 9;
83
84 // LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
85 // Settings.Global.LOW_POWER_MODE is on.
86 static final int PRIORITY_LOW_POWER_MODE = 10;
87
88 // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
89 // higher priority voters' result is a range, it will fix the rate to a single choice.
90 // It's used to avoid refresh rate switches in certain conditions which may result in the
91 // user seeing the display flickering when the switches occur.
92 static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 11;
93
94 // Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
95 static final int PRIORITY_SKIN_TEMPERATURE = 12;
96
97 // The proximity sensor needs the refresh rate to be locked in order to function, so this is
98 // set to a high priority.
99 static final int PRIORITY_PROXIMITY = 13;
100
101 // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
102 // to function, so this needs to be the highest priority of all votes.
103 static final int PRIORITY_UDFPS = 14;
104
105 // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
106 // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
107
108 static final int MIN_PRIORITY = PRIORITY_DEFAULT_RENDER_FRAME_RATE;
109 static final int MAX_PRIORITY = PRIORITY_UDFPS;
110
111 // The cutoff for the app request refresh rate range. Votes with priorities lower than this
112 // value will not be considered when constructing the app request refresh rate range.
113 static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
114 PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE;
...
Vote 对象包含以下属性。
显示 width(pixels);
显示 height(pixels);
刷新率范围(包含应用刷新率范围,屏幕刷新率范围);
是否禁止屏幕切换刷新率(屏幕刷新率范围是一个孤立的值);
应用设置的期望 base mode 刷新率。
java
// frameworks/base/services/core/java/com/android/server/display/mode/Vote.java
121 /**
122 * The requested width of the display in pixels, or INVALID_SIZE;
123 */
124 public final int width;
125 /**
126 * The requested height of the display in pixels, or INVALID_SIZE;
127 */
128 public final int height;
129 /**
130 * Information about the refresh rate frame rate ranges DM would like to set the display to.
131 */
132 public final SurfaceControl.RefreshRateRanges refreshRateRanges;
133
134 /**
135 * Whether refresh rate switching should be disabled (i.e. the refresh rate range is
136 * a single value).
137 */
138 public final boolean disableRefreshRateSwitching;
139
140 /**
141 * The preferred refresh rate selected by the app. It is used to validate that the summary
142 * refresh rate ranges include this value, and are not restricted by a lower priority vote.
143 */
144 public final float appRequestBaseModeRefreshRate;
Vote 对象有 5 种构造方式,这 5 种方式构造的 Vote 对象功能不同。
- Vote.forPhysicalRefreshRates 方法
构造的 Vote 不限制分辨率、应用刷新率范围(0~正无穷)、base mode 的刷新率;
限制屏幕刷新率范围;如果范围的 min == max,则禁止刷新率切换。
- Vote.forRenderFrameRates 方法
构造的 Vote 不限制分辨率、屏幕刷新率范围(0~正无穷)、base mode 的刷新率;
限制应用刷新率范围,不禁止刷新率切换。
- Vote.forSize 方法
仅限制分辨率(即 width、height 值)
- Vote.forDisableRefreshRateSwitching 方法
仅禁止刷新率切换
- Vote.forBaseModeRefreshRate 方法
仅设置应用设置的期望 base mode 刷新率
java
146 static Vote forPhysicalRefreshRates(float minRefreshRate, float maxRefreshRate) {
147 return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate, 0,
148 Float.POSITIVE_INFINITY,
149 minRefreshRate == maxRefreshRate, 0f);
150 }
151
152 static Vote forRenderFrameRates(float minFrameRate, float maxFrameRate) {
153 return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, minFrameRate,
154 maxFrameRate,
155 false, 0f);
156 }
157
158 static Vote forSize(int width, int height) {
159 return new Vote(width, height, 0, Float.POSITIVE_INFINITY, 0, Float.POSITIVE_INFINITY,
160 false,
161 0f);
162 }
163
164 static Vote forDisableRefreshRateSwitching() {
165 return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
166 Float.POSITIVE_INFINITY, true,
167 0f);
168 }
169
170 static Vote forBaseModeRefreshRate(float baseModeRefreshRate) {
171 return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
172 Float.POSITIVE_INFINITY, false,
173 baseModeRefreshRate);
174 }
175
176 private Vote(int width, int height,
177 float minPhysicalRefreshRate,
178 float maxPhysicalRefreshRate,
179 float minRenderFrameRate,
180 float maxRenderFrameRate,
181 boolean disableRefreshRateSwitching,
182 float baseModeRefreshRate) {
183 this.width = width;
184 this.height = height;
185 this.refreshRateRanges = new SurfaceControl.RefreshRateRanges(
186 new SurfaceControl.RefreshRateRange(minPhysicalRefreshRate, maxPhysicalRefreshRate),
187 new SurfaceControl.RefreshRateRange(minRenderFrameRate, maxRenderFrameRate));
188 this.disableRefreshRateSwitching = disableRefreshRateSwitching;
189 this.appRequestBaseModeRefreshRate = baseModeRefreshRate;
190 }
4.3.2.2 VotesStorage
VotesStorage 的核心是成员变量 mVotesByDisplay。
每个 display(id)可以包含多个 Votes,每个 Vote 对应一个策略(优先级),将这些 Votes 存为 map,即每个 display(id)对应一个 Votes map,其 entry 为 <priority,Vote>;
然后再将所有 display(id)的 Votes map 存为一个更大的 map,即 mVotesByDisplay,其 entry 为 <display id,map<prioriy, Vote>>。
java
// frameworks/base/services/core/java/com/android/server/display/mode/VotesStorage.java
29 class VotesStorage {
30 private static final String TAG = "VotesStorage";
...
40 // A map from the display ID to the collection of votes and their priority. The latter takes
41 // the form of another map from the priority to the vote itself so that each priority is
42 // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
43 @GuardedBy("mStorageLock")
44 private final SparseArray<SparseArray<Vote>> mVotesByDisplay = new SparseArray<>();
...
GLOBAL_ID(-1)表示全局策略,当全局策略中包含某个 priority,而目标 display id 不包含该 priority,则会使用全局的 priority 策略。也就是说目标 display id 的策略优先级更高。
更新 Votes map。
-
updateGlobalVote 方法更新 GLOBAL_ID 即全局的 Votes map。
-
updateVote 方法更新目标 display id 的 Votes map。
java
// frameworks/base/services/core/java/com/android/server/display/mode/VotesStorage.java
75 /** updates vote storage for all displays */
76 void updateGlobalVote(int priority, @Nullable Vote vote) {
77 updateVote(GLOBAL_ID, priority, vote);
78 }
79
80 /** updates vote storage */
81 void updateVote(int displayId, int priority, @Nullable Vote vote) {
82 if (mLoggingEnabled) {
83 Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
84 + ", priority=" + Vote.priorityToString(priority)
85 + ", vote=" + vote + ")");
86 }
87 if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
88 Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
89 + " priority=" + Vote.priorityToString(priority)
90 + ", vote=" + vote);
91 return;
92 }
93 SparseArray<Vote> votes;
94 synchronized (mStorageLock) {
95 if (mVotesByDisplay.contains(displayId)) {
96 votes = mVotesByDisplay.get(displayId);
97 } else {
98 votes = new SparseArray<>();
99 mVotesByDisplay.put(displayId, votes);
100 }
101 if (vote != null) {
102 votes.put(priority, vote);
103 } else {
104 votes.remove(priority);
105 }
106 }
107 if (mLoggingEnabled) {
108 Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes);
109 }
// 回调
110 mListener.onChanged();
111 }
112
VotesStorage 更新策略后,会回调监听器的 onChanged 方法。
我们先看下 VotesStorage 监听器是怎么注册的,以及监听器的 onChanged 方法做了什么。
DisplayModeDirector 初始化时构造 VotesStorage 对象,同时注册监听器,监听器的 onChanged 方法调用 DisplayModeDirector .notifyDesiredDisplayModeSpecsChangedLocked。
也就是说,VotesStorage 更新后,会回调 DisplayModeDirector .notifyDesiredDisplayModeSpecsChangedLocked 方法。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
152 public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
153 @NonNull Injector injector) {
154 mContext = context;
155 mHandler = new DisplayModeDirectorHandler(handler.getLooper());
156 mInjector = injector;
157 mSupportedModesByDisplay = new SparseArray<>();
158 mDefaultModeByDisplay = new SparseArray<>();
159 mAppRequestObserver = new AppRequestObserver();
160 mDeviceConfig = injector.getDeviceConfig();
161 mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
162 mSettingsObserver = new SettingsObserver(context, handler);
163 mBrightnessObserver = new BrightnessObserver(context, handler, injector);
164 mDefaultDisplayDeviceConfig = null;
165 mUdfpsObserver = new UdfpsObserver();
// 构造 VotesStorage,设置回调
166 mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked);
167 mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage);
168 mSensorObserver = new SensorObserver(context, mVotesStorage, injector);
169 mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, mVotesStorage);
170 mHbmObserver = new HbmObserver(injector, mVotesStorage, BackgroundThread.getHandler(),
171 mDeviceConfigDisplaySettings);
172 mAlwaysRespectAppRequest = false;
173 mSupportsFrameRateOverride = injector.supportsFrameRateOverride();
174 }
175
java
// frameworks/base/services/core/java/com/android/server/display/mode/VotesStorage.java
29 class VotesStorage {
37 private final Listener mListener;
45
46 VotesStorage(@NonNull Listener listener) {
47 mListener = listener;
48 }
...
149 interface Listener {
150 void onChanged();
151 }
152 }
注意,这里涉及到 Java 的方法引用和 函数 式编程特性。VotesStorage 的构造函数参数传递的是一个方法引用 this::notifyDesiredDisplayModeSpecsChangedLocked。
方法引用 this::notifyDesiredDisplayModeSpecsChangedLocked 是 Java 8 引入的一种语法,用于简化 Lambda 表达式。在这种情况下,它引用了 DisplayModeDirector 类中的 notifyDesiredDisplayModeSpecsChangedLocked 方法,并将其传递给 VotesStorage 构造函数。
这相当于创建了一个 Listener 接口的匿名实现类对象,该对象的 onChanged() 方法实现调用的是 notifyDesiredDisplayModeSpecsChangedLocked 方法。
相当于:
java
mVotesStorage = new VotesStorage(new Listener() {@Overridepublic void onChanged() {
notifyDesiredDisplayModeSpecsChangedLocked();
}
});
返回目标 display id 的 Votes map。
java
// frameworks/base/services/core/java/com/android/server/display/mode/VotesStorage.java
53 /**
54 * gets all votes for specific display, note that global display votes are also added to result
55 */
56 @NonNull
57 SparseArray<Vote> getVotes(int displayId) {
58 SparseArray<Vote> votesLocal;
59 SparseArray<Vote> globalVotesLocal;
60 synchronized (mStorageLock) {
// 复制目标 display id 的 Votes map,即 temp Votes map 1
61 SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
62 votesLocal = displayVotes != null ? displayVotes.clone() : new SparseArray<>();
// 复制 GLOBAL_ID 的 Votes map,即 temp Votes map 2
63 SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
64 globalVotesLocal = globalVotes != null ? globalVotes.clone() : new SparseArray<>();
65 }
// 合并 temp Votes map 1 和 temp Votes map 2.
// 即将 GLOBAL_ID 包含而目标 display id 不包含的 priority Vote 合并到返回的 Votes map
66 for (int i = 0; i < globalVotesLocal.size(); i++) {
67 int priority = globalVotesLocal.keyAt(i);
68 if (!votesLocal.contains(priority)) {
69 votesLocal.put(priority, globalVotesLocal.valueAt(i));
70 }
71 }
72 return votesLocal;
73 }
4.3.3 DisplayModeDirector.updateRefreshRateSettingLocked
DisplayModeDirector.updateRefreshRateSettingLocked 方法根据 "最低刷新率"、"默认刷新率"、"默认的用户设置刷新率", "用户设置的刷新率" 这些设置计算得到 "用户(user)设置的最低策 略"、"默认策略 "、"用户(user)设置的策略" 的策略对象(Votes),并调用 VotesStorage.updateGlobalVote 更新全局刷新率策略。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
1207 private void updateRefreshRateSettingLocked() {
1208 final ContentResolver cr = mContext.getContentResolver();
// 最低刷新率
1209 float minRefreshRate = Settings.System.getFloatForUser(cr,
1210 Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
// 用户设置的刷新率
1211 float peakRefreshRate = Settings.System.getFloatForUser(cr,
1212 Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId());
// 更新 global 策略
1213 updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
1214 }
1215
1216 private void updateRefreshRateSettingLocked(
1217 float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
1218 // TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is
1219 // used to predict if we're going to be doing frequent refresh rate switching, and if
1220 // so, enable the brightness observer. The logic here is more complicated and fragile
1221 // than necessary, and we should improve it. See b/156304339 for more info.
1222 Vote peakVote = peakRefreshRate == 0f
1223 ? null
1224 : Vote.forRenderFrameRates(0f, Math.max(minRefreshRate, peakRefreshRate));
// 更新 global "用户(user)设置的策略"
1225 mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
1226 peakVote);
// 更新 global "用户(user)设置的最低策略"
1227 mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
1228 Vote.forRenderFrameRates(minRefreshRate, Float.POSITIVE_INFINITY));
1229 Vote defaultVote =
1230 defaultRefreshRate == 0f
1231 ? null : Vote.forRenderFrameRates(0f, defaultRefreshRate);
// 更新 global "默认策略"
1232 mVotesStorage.updateGlobalVote(Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE, defaultVote);
1233
1234 float maxRefreshRate;
1235 if (peakRefreshRate == 0f && defaultRefreshRate == 0f) {
1236 // We require that at least one of the peak or default refresh rate values are
1237 // set. The brightness observer requires that we're able to predict whether or not
1238 // we're going to do frequent refresh rate switching, and with the way the code is
1239 // currently written, we need either a default or peak refresh rate value for that.
1240 Slog.e(TAG, "Default and peak refresh rates are both 0. One of them should be set"
1241 + " to a valid value.");
1242 maxRefreshRate = minRefreshRate;
1243 } else if (peakRefreshRate == 0f) {
1244 maxRefreshRate = defaultRefreshRate;
1245 } else if (defaultRefreshRate == 0f) {
1246 maxRefreshRate = peakRefreshRate;
1247 } else {
1248 maxRefreshRate = Math.min(defaultRefreshRate, peakRefreshRate);
1249 }
1250
1251 mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, maxRefreshRate);
1252 }
4.3.4 DisplayModeDirector .notifyDesiredDisplayModeSpecsChangedLocked
上文提到过,VotesStorage 更新策略(不管是 VotesStorage.updateGlobalVote 还是 VotesStorage.updateVote)后会回调 DisplayModeDirector .notifyDesiredDisplayModeSpecsChangedLocked 方法。
notifyDesiredDisplayModeSpecsChangedLocked 方法发送 msg MSG_REFRESH_RATE_RANGE_CHANGED 和 obj mDesiredDisplayModeSpecsListener 到 handler。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
798 private void notifyDesiredDisplayModeSpecsChangedLocked() {
799 if (mDesiredDisplayModeSpecsListener != null
800 && !mHandler.hasMessages(MSG_REFRESH_RATE_RANGE_CHANGED)) {
801 // We need to post this to a handler to avoid calling out while holding the lock
802 // since we know there are things that both listen for changes as well as provide
803 // information. If we did call out while holding the lock, then there's no
804 // guaranteed lock order and we run the real of risk deadlock.
805 Message msg = mHandler.obtainMessage(
806 MSG_REFRESH_RATE_RANGE_CHANGED, mDesiredDisplayModeSpecsListener);
807 msg.sendToTarget();
808 }
809 }
handler 收到 msg MSG_REFRESH_RATE_RANGE_CHANGED 时调用 mDesiredDisplayModeSpecsListener.onDesiredDisplayModeSpecsChanged 方法。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
876 /**
877 * Listens for changes refresh rate coordination.
878 */
879 public interface DesiredDisplayModeSpecsListener {
880 /**
881 * Called when the refresh rate range may have changed.
882 */
883 void onDesiredDisplayModeSpecsChanged();
884 }
885
886 private final class DisplayModeDirectorHandler extends Handler {
887 DisplayModeDirectorHandler(Looper looper) {
888 super(looper, null, true /*async*/);
889 }
890
891 @Override
892 public void handleMessage(Message msg) {
893 switch (msg.what) {
...
930 case MSG_REFRESH_RATE_RANGE_CHANGED:
931 DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener =
932 (DesiredDisplayModeSpecsListener) msg.obj;
933 desiredDisplayModeSpecsListener.onDesiredDisplayModeSpecsChanged();
934 break;
...
948 }
949 }
950 }
mDesiredDisplayModeSpecsListener 是一个显示参数监听器,
由 DisplayModeDirector.setDesiredDisplayModeSpecsListener 方法设置。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
661 /**
662 * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate ranges.
663 */
664 public void setDesiredDisplayModeSpecsListener(
665 @Nullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener) {
666 synchronized (mLock) {
667 mDesiredDisplayModeSpecsListener = desiredDisplayModeSpecsListener;
668 }
669 }
DMS 启动时,创建一个目标显示参数的监听器,注册到 DisplayModeDirector。
当目标显示参数变化时,将显示参数设置到 LocalDisplayDevice。
java
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
684 /**
685 * Called when the system is ready to go.
686 */
687 public void systemReady(boolean safeMode) {
688 synchronized (mSyncRoot) {
707
...
// 设置监听器
708 mDisplayModeDirector.setDesiredDisplayModeSpecsListener(
709 new DesiredDisplayModeSpecsObserver());
...
4641 class DesiredDisplayModeSpecsObserver
4642 implements DisplayModeDirector.DesiredDisplayModeSpecsListener {
4643
4644 private final Consumer<LogicalDisplay> mSpecsChangedConsumer = display -> {
4645 int displayId = display.getDisplayIdLocked();
// 从 DisplayModeDirector 获取显示参数(DisplayModeDirector$DesiredDisplayModeSpecs 结构)
4646 DisplayModeDirector.DesiredDisplayModeSpecs desiredDisplayModeSpecs =
4647 mDisplayModeDirector.getDesiredDisplayModeSpecs(displayId);
4648 DisplayModeDirector.DesiredDisplayModeSpecs existingDesiredDisplayModeSpecs =
4649 display.getDesiredDisplayModeSpecsLocked();
4650 if (DEBUG) {
4651 Slog.i(TAG,
4652 "Comparing display specs: " + desiredDisplayModeSpecs
4653 + ", existing: " + existingDesiredDisplayModeSpecs);
4654 }
4655 if (!desiredDisplayModeSpecs.equals(existingDesiredDisplayModeSpecs)) {
// 将显示参数设置到 LocalDisplayDevice
4656 display.setDesiredDisplayModeSpecsLocked(desiredDisplayModeSpecs);
4657 mChanged = true;
4658 }
4659 };
4660
4661 @GuardedBy("mSyncRoot")
4662 private boolean mChanged = false;
4663
// 显示参数变化,监听器回调
4664 public void onDesiredDisplayModeSpecsChanged() {
4665 synchronized (mSyncRoot) {
4666 mChanged = false;
4667 mLogicalDisplayMapper.forEachLocked(mSpecsChangedConsumer,
4668 /* includeDisabled= */ false);
4669 if (mChanged) {
4670 scheduleTraversalLocked(false);
4671 mChanged = false;
4672 }
4673 }
4674 }
4675 }
将显示参数设置到 LocalDisplayDevice
-
由 DisplayModeDirector$DesiredDisplayModeSpecs.baseModeId 得到目标 base mode id
-
将目标 base mode id 连同 DisplayModeDirectorDesiredDisplayModeSpecs.primary、DisplayModeDirectorDesiredDisplayModeSpecs.appRequest 一起封装到 SurfaceControl$DesiredDisplayModeSpecs 结构,并设置到 SurfaceFlinger。mSurfaceControlProxy.setDesiredDisplayModeSpecs 调用 native 方法 nativeSetDesiredDisplayModeSpecs。
java
// frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java
191 private final class LocalDisplayDevice extends DisplayDevice {
... ...
989 @Override
990 public void setDesiredDisplayModeSpecsLocked(
991 DisplayModeDirector.DesiredDisplayModeSpecs displayModeSpecs) {
992 if (displayModeSpecs.baseModeId == 0) {
993 // Bail if the caller is requesting a null mode. We'll get called again shortly with
994 // a valid mode.
995 return;
996 }
997
998 // Find the mode Id based on the desired mode specs. In case there is more than one
999 // mode matching the mode spec, prefer the one that is in the default mode group.
1000 // For now the default config mode is taken from the active mode when we got the
1001 // hotplug event for the display. In the future we might want to change the default
1002 // mode based on vendor requirements.
1003 // Note: We prefer the default mode group over the current one as this is the mode
1004 // group the vendor prefers.
1005 int baseSfModeId = findSfDisplayModeIdLocked(displayModeSpecs.baseModeId,
1006 mDefaultModeGroup);
1007 if (baseSfModeId < 0) {
1008 // When a display is hotplugged, it's possible for a mode to be removed that was
1009 // previously valid. Because of the way display changes are propagated through the
1010 // framework, and the caching of the display mode specs in LogicalDisplay, it's
1011 // possible we'll get called with a stale mode id that no longer represents a valid
1012 // mode. This should only happen in extremely rare cases. A followup call will
1013 // contain a valid mode id.
1014 Slog.w(TAG,
1015 "Ignoring request for invalid base mode id " + displayModeSpecs.baseModeId);
1016 updateDeviceInfoLocked();
1017 return;
1018 }
1019 if (mDisplayModeSpecsInvalid || !displayModeSpecs.equals(mDisplayModeSpecs)) {
1020 mDisplayModeSpecsInvalid = false;
1021 mDisplayModeSpecs.copyFrom(displayModeSpecs);
1022 getHandler().sendMessage(PooledLambda.obtainMessage(
1023 LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
1024 getDisplayTokenLocked(),
1025 new SurfaceControl.DesiredDisplayModeSpecs(baseSfModeId,
1026 mDisplayModeSpecs.allowGroupSwitching,
1027 mDisplayModeSpecs.primary,
1028 mDisplayModeSpecs.appRequest)));
1029 }
1030 }
1031
1032 private void setDesiredDisplayModeSpecsAsync(IBinder displayToken,
1033 SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
1034 // Do not lock when calling these SurfaceControl methods because they are sync
1035 // operations that may block for a while when setting display power mode.
1036 mSurfaceControlProxy.setDesiredDisplayModeSpecs(displayToken, modeSpecs);
1037 }
这里的关键是,调用 DisplayModeDirector.getDesiredDisplayModeSpecs 方法来获取目标显示参数,即得到 DisplayModeDirector$DesiredDisplayModeSpecs 结构。
而 DisplayModeDirector.getDesiredDisplayModeSpecs 方法实际上又是从 VotesStorage 获取目标显示参数的。
下面一个章节就介绍 VotesStorage 中的策略如何影响 DisplayModeDirector.getDesiredDisplayModeSpecs 的返回值。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
365 /**
366 * Calculates the refresh rate ranges and display modes that the system is allowed to freely
367 * switch between based on global and display-specific constraints.
368 *
369 * @param displayId The display to query for.
370 * @return The ID of the default mode the system should use, and the refresh rate range the
371 * system is allowed to switch between.
372 */
373 @NonNull
374 public DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(int displayId) {
375 synchronized (mLock) {
376 SparseArray<Vote> votes = mVotesStorage.getVotes(displayId);
377 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
378 Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
... ...
4.3.5 DisplayModeDirector.getDesiredDisplayModeSpecs
DisplayModeDirector.getDesiredDisplayModeSpecs 方法返回一个 DisplayModeDirector$DesiredDisplayModeSpecs 对象。
DisplayModeDirector.getDesiredDisplayModeSpecs 方法的核心是 summarizeVotes。
summarizeVotes 方法返回一个 VoteSummary 对象。
VoteSummary 表示对一组 Votes 策略的整合,整合结果包含目标: "屏幕刷新率" 范围、"应用刷新率"范围、以及 base mode 的刷新率。
DesiredDisplayModeSpecs 对象是对目标 base mode、 "屏幕刷新率" 范围、"应用刷新率" 范围的封装。
DisplayModeDirector.getDesiredDisplayModeSpecs 方法,
先调用 summarizeVotes 方法将 priority 0~15 的所有 Votes 策略整合为 primarySummary(如果支持的 display modes 中没有任何一个 mode 可以满足所有 Votes 策略的要求,则整合 priority 1~15,依此类推),primarySummary 中如果包含 "base mode 的刷新率",则找一个符合要求的 mode 作为 base mode。
然后再次调用 summarizeVotes 方法将 priority 4~15 的 Votes 策略整合为 appRequestSummary。
先分析 summarizeVotes 方法的作用。
-
入参 SparseArray<Vote> votes 是目标 Votes map
-
入参 int lowestConsideredPriority 和 int highestConsideredPriority 表示目标 priority 范围
-
入参 VoteSummary summary 是 out 输出
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
258 // VoteSummary is returned as an output param to cut down a bit on the number of temporary
259 // objects.
260 private void summarizeVotes(
261 SparseArray<Vote> votes,
262 int lowestConsideredPriority,
263 int highestConsideredPriority,
264 /*out*/ VoteSummary summary) {
265 summary.reset();
266 for (int priority = highestConsideredPriority;
267 priority >= lowestConsideredPriority;
268 priority--) {
269 Vote vote = votes.get(priority);
270 if (vote == null) {
271 continue;
272 }
273
274
275 // For physical refresh rates, just use the tightest bounds of all the votes.
276 // The refresh rate cannot be lower than the minimal render frame rate.
277 final float minPhysicalRefreshRate = Math.max(vote.refreshRateRanges.physical.min,
278 vote.refreshRateRanges.render.min);
279 summary.minPhysicalRefreshRate = Math.max(summary.minPhysicalRefreshRate,
280 minPhysicalRefreshRate);
281 summary.maxPhysicalRefreshRate = Math.min(summary.maxPhysicalRefreshRate,
282 vote.refreshRateRanges.physical.max);
283
284 // Same goes to render frame rate, but frame rate cannot exceed the max physical
285 // refresh rate
286 final float maxRenderFrameRate = Math.min(vote.refreshRateRanges.render.max,
287 vote.refreshRateRanges.physical.max);
288 summary.minRenderFrameRate = Math.max(summary.minRenderFrameRate,
289 vote.refreshRateRanges.render.min);
290 summary.maxRenderFrameRate = Math.min(summary.maxRenderFrameRate, maxRenderFrameRate);
291
292 // For display size, disable refresh rate switching and base mode refresh rate use only
293 // the first vote we come across (i.e. the highest priority vote that includes the
294 // attribute).
295 if (summary.height == Vote.INVALID_SIZE && summary.width == Vote.INVALID_SIZE
296 && vote.height > 0 && vote.width > 0) {
297 summary.width = vote.width;
298 summary.height = vote.height;
299 }
300 if (!summary.disableRefreshRateSwitching && vote.disableRefreshRateSwitching) {
301 summary.disableRefreshRateSwitching = true;
302 }
303 if (summary.appRequestBaseModeRefreshRate == 0f
304 && vote.appRequestBaseModeRefreshRate > 0f) {
305 summary.appRequestBaseModeRefreshRate = vote.appRequestBaseModeRefreshRate;
306 }
307
308 if (mLoggingEnabled) {
309 Slog.w(TAG, "Vote summary for priority " + Vote.priorityToString(priority)
310 + ": " + summary);
311 }
312 }
313 }
summarizeVotes 方法
-
对 Votes 的 "屏幕刷新率" 范围取交集,成为目标 "屏幕刷新率"
-
对 Votes 的 "应用刷新率" 范围取交集,成为目标 "应用刷新率"
-
如果 Votes 包含具有 "应用期望的 base mode 刷新率" 的 Vote,将其中 priority 最高的设置为目标 "base mode 刷新率"
注意,如果目标 Votes map 中没有满足目标 priority 范围的 Vote,返回的 VoteSummary 对象的刷新率范围就是 [0,无限大]
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
220 private static final class VoteSummary {
221 public float minPhysicalRefreshRate;
222 public float maxPhysicalRefreshRate;
223 public float minRenderFrameRate;
224 public float maxRenderFrameRate;
225 public int width;
226 public int height;
227 public boolean disableRefreshRateSwitching;
228 public float appRequestBaseModeRefreshRate;
229
230 VoteSummary() {
231 reset();
232 }
233
234 public void reset() {
235 minPhysicalRefreshRate = 0f;
236 maxPhysicalRefreshRate = Float.POSITIVE_INFINITY;
237 minRenderFrameRate = 0f;
238 maxRenderFrameRate = Float.POSITIVE_INFINITY;
239 width = Vote.INVALID_SIZE;
240 height = Vote.INVALID_SIZE;
241 disableRefreshRateSwitching = false;
242 appRequestBaseModeRefreshRate = 0f;
243 }
...
DesiredDisplayModeSpecs getDesiredDisplayModeSpecs 方法代码比较长,下面分成 7 段分析。
第一段。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
365 /**
366 * Calculates the refresh rate ranges and display modes that the system is allowed to freely
367 * switch between based on global and display-specific constraints.
368 *
369 * @param displayId The display to query for.
370 * @return The ID of the default mode the system should use, and the refresh rate range the
371 * system is allowed to switch between.
372 */
373 @NonNull
374 public DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(int displayId) {
375 synchronized (mLock) {
// 获取目标 display id 的 Votes map
376 SparseArray<Vote> votes = mVotesStorage.getVotes(displayId);
// 目标 display id 支持的模式(modes)列表
377 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
// 目标 display id 默认的模式(mode)
378 Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
379 if (modes == null || defaultMode == null) {
380 Slog.e(TAG,
381 "Asked about unknown display, returning empty display mode specs!"
382 + "(id=" + displayId + ")");
383 return new DesiredDisplayModeSpecs();
384 }
385
386 ArrayList<Display.Mode> availableModes = new ArrayList<>();
387 availableModes.add(defaultMode);
388 VoteSummary primarySummary = new VoteSummary();
389 int lowestConsideredPriority = Vote.MIN_PRIORITY;
390 int highestConsideredPriority = Vote.MAX_PRIORITY;
391
392 if (mAlwaysRespectAppRequest) {
393 lowestConsideredPriority = Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE;
394 highestConsideredPriority = Vote.PRIORITY_APP_REQUEST_SIZE;
395 }
396
第二段。
从 [Vote.MIN_PRIORITY,Vote.MAX_PRIORITY] 至 [Vote.MAX_PRIORITY,Vote.MAX_PRIORITY] 逐步缩小 priority 范围,调用 summarizeVotes 方法,直到 summarizeVotes 返回的 VoteSummary 能与目标 display id 支持的模式(modes)匹配:
-
显示设备支持多种模式,每种模式有固定的刷新率。
-
目标是寻找一个与我们的 Votes map 尽可能匹配的显示模式(至少要匹配最高优先级 priority 的 Vote)。
-
最优的情况当然是这个显示模式满足所有 priority 的 Votes 期望,所以一开始匹配的是 [Vote.MIN_PRIORITY,Vote.MAX_PRIORITY] 范围内的 Votes 交集。
-
最差的情况是只要匹配最高优先级(Vote.MAX_PRIORITY)的 Vote 就行。
-
最最差的情况是匹配不到,即 availableModes 为空。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
397 // We try to find a range of priorities which define a non-empty set of allowed display
398 // modes. Each time we fail we increase the lowest priority.
399 while (lowestConsideredPriority <= highestConsideredPriority) {
// (1)
400 summarizeVotes(
401 votes, lowestConsideredPriority, highestConsideredPriority, primarySummary);
402
403 // If we don't have anything specifying the width / height of the display, just use
404 // the default width and height. We don't want these switching out from underneath
405 // us since it's a pretty disruptive behavior.
406 if (primarySummary.height == Vote.INVALID_SIZE
407 || primarySummary.width == Vote.INVALID_SIZE) {
408 primarySummary.width = defaultMode.getPhysicalWidth();
409 primarySummary.height = defaultMode.getPhysicalHeight();
410 }
411
// (2)
412 availableModes = filterModes(modes, primarySummary);
// (3)
413 if (!availableModes.isEmpty()) {
414 if (mLoggingEnabled) {
415 Slog.w(TAG, "Found available modes=" + availableModes
416 + " with lowest priority considered "
417 + Vote.priorityToString(lowestConsideredPriority)
418 + " and constraints: "
419 + "width=" + primarySummary.width
420 + ", height=" + primarySummary.height
421 + ", minPhysicalRefreshRate="
422 + primarySummary.minPhysicalRefreshRate
423 + ", maxPhysicalRefreshRate="
424 + primarySummary.maxPhysicalRefreshRate
425 + ", minRenderFrameRate=" + primarySummary.minRenderFrameRate
426 + ", maxRenderFrameRate=" + primarySummary.maxRenderFrameRate
427 + ", disableRefreshRateSwitching="
428 + primarySummary.disableRefreshRateSwitching
429 + ", appRequestBaseModeRefreshRate="
430 + primarySummary.appRequestBaseModeRefreshRate);
431 }
432 break;
433 }
434
435 if (mLoggingEnabled) {
436 Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
437 + Vote.priorityToString(lowestConsideredPriority)
438 + " and with the following constraints: "
439 + "width=" + primarySummary.width
440 + ", height=" + primarySummary.height
441 + ", minPhysicalRefreshRate=" + primarySummary.minPhysicalRefreshRate
442 + ", maxPhysicalRefreshRate=" + primarySummary.maxPhysicalRefreshRate
443 + ", minRenderFrameRate=" + primarySummary.minRenderFrameRate
444 + ", maxRenderFrameRate=" + primarySummary.maxRenderFrameRate
445 + ", disableRefreshRateSwitching="
446 + primarySummary.disableRefreshRateSwitching
447 + ", appRequestBaseModeRefreshRate="
448 + primarySummary.appRequestBaseModeRefreshRate);
449 }
450
451 // If we haven't found anything with the current set of votes, drop the
452 // current lowest priority vote.
// (4)
453 lowestConsideredPriority++;
454 }
第三段。
调用 summarizeVotes 方法,返回表示 [Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF,Vote.MAX_PRIORITY] priority 范围内 Votes 策略交集的 VoteSummary,即 appRequestSummary。
APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF 的值是 4,优先级高于 "用户(user)设置的最低策略(3)",低于 "应用(app)设置的 base mode 刷新率策略(5)"和 "用户(user)设置的策略(7)"。
APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF = PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 4。
注意到,summarizeVotes 返回的 appRequestSummary 可能是 primarySummary 的子集,也可能是 primarySummary 的父集。
而在 summarizeVotes 返回后,会对 appRequestSummary 做一个调整,保证 appRequestSummary 是 primarySummary 的父集。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
455
456 if (mLoggingEnabled) {
457 Slog.i(TAG,
458 "Primary physical range: ["
459 + primarySummary.minPhysicalRefreshRate
460 + " "
461 + primarySummary.maxPhysicalRefreshRate
462 + "] render frame rate range: ["
463 + primarySummary.minRenderFrameRate
464 + " "
465 + primarySummary.maxRenderFrameRate
466 + "]");
467 }
468
// (1)
469 VoteSummary appRequestSummary = new VoteSummary();
470 summarizeVotes(
471 votes,
472 Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF,
473 Vote.MAX_PRIORITY,
474 appRequestSummary);
// (2) 调整 appRequestSummary 的目标范围,保证 appRequestSummary 是 primarySummary 的父集
475 appRequestSummary.minPhysicalRefreshRate =
476 Math.min(appRequestSummary.minPhysicalRefreshRate,
477 primarySummary.minPhysicalRefreshRate);
478 appRequestSummary.maxPhysicalRefreshRate =
479 Math.max(appRequestSummary.maxPhysicalRefreshRate,
480 primarySummary.maxPhysicalRefreshRate);
481 appRequestSummary.minRenderFrameRate =
482 Math.min(appRequestSummary.minRenderFrameRate,
483 primarySummary.minRenderFrameRate);
484 appRequestSummary.maxRenderFrameRate =
485 Math.max(appRequestSummary.maxRenderFrameRate,
486 primarySummary.maxRenderFrameRate);
487 if (mLoggingEnabled) {
488 Slog.i(TAG,
489 "App request range: ["
490 + appRequestSummary.minPhysicalRefreshRate
491 + " "
492 + appRequestSummary.maxPhysicalRefreshRate
493 + "] Frame rate range: ["
494 + appRequestSummary.minRenderFrameRate
495 + " "
496 + appRequestSummary.maxRenderFrameRate
497 + "]");
498 }
499
第四段。
调用 selectBaseMode 方法,从 availableModes 中选择一个 base mode。
availableModes 即通过第二段代码过滤出的匹配 Votes 策略(primarySummary)的 modes。
selectBaseMode 方法在 availableModes 中,
-
选择一个刷新率与应用设置的期望 base mode 刷新率(primarySummary.appRequestBaseModeRefreshRate)相同的 mode 作为 base mode;
-
如果应用没有设置期望 base mode 刷新率,则选择一个与 defaultMode 拥有相同刷新率的 mode 作为 base mode
-
如果 availableModes 列表中没有满足条件的 mode,则选择列表中首个 mode 作为 base mode
-
如果 availableModes 为空,则返回 null
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
// (1)
500 Display.Mode baseMode = selectBaseMode(primarySummary, availableModes, defaultMode);
501 if (baseMode == null) {
502 Slog.w(TAG, "Can't find a set of allowed modes which satisfies the votes. Falling"
503 + " back to the default mode. Display = " + displayId + ", votes = " + votes
504 + ", supported modes = " + Arrays.toString(modes));
505
506 float fps = defaultMode.getRefreshRate();
507 final RefreshRateRange range = new RefreshRateRange(fps, fps);
508 final RefreshRateRanges ranges = new RefreshRateRanges(range, range);
509 return new DesiredDisplayModeSpecs(defaultMode.getModeId(),
510 /*allowGroupSwitching */ false,
511 ranges, ranges);
512 }
513
selectBaseMode 方法代码如下:
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
315 private boolean equalsWithinFloatTolerance(float a, float b) {
316 return a >= b - FLOAT_TOLERANCE && a <= b + FLOAT_TOLERANCE;
317 }
318
319 private Display.Mode selectBaseMode(VoteSummary summary,
320 ArrayList<Display.Mode> availableModes, Display.Mode defaultMode) {
321 // The base mode should be as close as possible to the app requested mode. Since all the
322 // available modes already have the same size, we just need to look for a matching refresh
323 // rate. If the summary doesn't include an app requested refresh rate, we'll use the default
324 // mode refresh rate. This is important because SurfaceFlinger can do only seamless switches
325 // by default. Some devices (e.g. TV) don't support seamless switching so the mode we select
326 // here won't be changed.
327 float preferredRefreshRate =
328 summary.appRequestBaseModeRefreshRate > 0
329 ? summary.appRequestBaseModeRefreshRate : defaultMode.getRefreshRate();
330 for (Display.Mode availableMode : availableModes) {
331 if (equalsWithinFloatTolerance(preferredRefreshRate, availableMode.getRefreshRate())) {
332 return availableMode;
333 }
334 }
335
336 // If we couldn't find a mode id based on the refresh rate, it means that the available
337 // modes were filtered by the app requested size, which is different that the default mode
338 // size, and the requested app refresh rate was dropped from the summary due to a higher
339 // priority vote. Since we don't have any other hint about the refresh rate,
340 // we just pick the first.
341 return !availableModes.isEmpty() ? availableModes.get(0) : null;
342 }
第五段。
处理 "禁止 mode switch"。
"禁止 mode switch" 有三种:
-
mModeSwitchingType 设置为 DisplayManager.SWITCHING_TYPE_NONE(最强)
这种情况下,会调较 primarySummary 和 appRequestSummary,使 primarySummary 和 appRequestSummary 固定 "屏幕刷新率" 和 "应用刷新率"。即,屏幕刷新率和平台的渲染帧率不会自动变化
-
mModeSwitchingType 设置为 DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY(次强)
这种情况下,会调较 primarySummary 和 appRequestSummary,使 primarySummary 固定 "屏幕刷新率" 和 "应用刷新率" ,使 appRequestSummary 固定 "屏幕刷新率"。即,屏幕刷新率不会自动变化,但平台的渲染帧率可能会自动变化
-
primarySummary.disableRefreshRateSwitching 为 true(如果 Votes map 中有一个用 forBaseModeRefreshRate 方法构造的 Vote,这些 Votes 的 summary 的 disableRefreshRateSwitching 就会是 true)
这种情况下,会调较 primarySummary,使该 primarySummary 固定 "屏幕刷新率"。即屏幕刷新率和平台的渲染帧率都可能会自动变化
java
514 boolean modeSwitchingDisabled =
515 mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE
516 || mModeSwitchingType
517 == DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY;
518
519 if (modeSwitchingDisabled || primarySummary.disableRefreshRateSwitching) {
520 float fps = baseMode.getRefreshRate();
521 disableModeSwitching(primarySummary, fps);
522 if (modeSwitchingDisabled) {
523 disableModeSwitching(appRequestSummary, fps);
524 disableRenderRateSwitching(primarySummary, fps);
525
526 if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
527 disableRenderRateSwitching(appRequestSummary, fps);
528 }
529 }
530 }
531
disableModeSwitching 方法的作用是将目标 VoteSummary 调较为禁止 mode switch ,
关键是将目标 VoteSummary 的屏幕刷新率固定,从而不需要切换显示 mode。
其做法是,
-
将 VoteSummary 的屏幕刷新率固定为目标 fps;
-
如果 VoteSummary 的最大渲染帧率高于目标 fps,则将最大渲染帧率设置为目标 fps。
例如,
假设 VoteSummary 原本的屏幕刷新率范围是 [90,120],渲染帧率要求范围是 [50, 90],base mode 的刷新率是 90
-
屏幕刷新率范围会调整为 [90, 90]
-
渲染帧率范围不调整,仍为 [50, 90]
假如 VoteSummary 原本的屏幕刷新率范围是 [60,90],渲染帧率要求范围是 [50, 90],base mode 的刷新率是 60
-
屏幕刷新率范围会调整为 [60, 60]
-
渲染帧率范围调整为 [50,60]
disableRenderRateSwitching 方法的作用是将目标 VoteSummary 调较为禁止渲染帧率 switch。
其做法是
-
首先修改 VoteSummary 的渲染帧率范围最大值,修改成与最小值一样;
-
检查目标 fps(base mode 的刷新率)是否与 VoteSummary 的渲染帧率要求匹配,简单说就是目标 fps 能够被渲染帧率整除。如果不能,则将 VoteSummary 的渲染帧率范围最大值、最小值都修改为 目标 fps。
例如
假设 VoteSummary 渲染帧率要求范围是 [50, 90],base mode 的刷新率是 90
- 将渲染帧率范围调整为 [90, 90]
假设 VoteSummary 渲染帧率要求范围是 [50, 90],base mode 的刷新率是 60
-
先将渲染帧率范围调整为 [90, 90]
-
后将渲染帧率范围调整为 [60, 60]
注:这里的渲染帧率即应用刷新率。
java
344 private void disableModeSwitching(VoteSummary summary, float fps) {
345 summary.minPhysicalRefreshRate = summary.maxPhysicalRefreshRate = fps;
346 summary.maxRenderFrameRate = Math.min(summary.maxRenderFrameRate, fps);
347
348 if (mLoggingEnabled) {
349 Slog.i(TAG, "Disabled mode switching on summary: " + summary);
350 }
351 }
352
353 private void disableRenderRateSwitching(VoteSummary summary, float fps) {
354 summary.minRenderFrameRate = summary.maxRenderFrameRate;
355
356 if (!isRenderRateAchievable(fps, summary)) {
357 summary.minRenderFrameRate = summary.maxRenderFrameRate = fps;
358 }
359
360 if (mLoggingEnabled) {
361 Slog.i(TAG, "Disabled render rate switching on summary: " + summary);
362 }
363 }
第六段。
处理 "允许 group switch"。
java
532 boolean allowGroupSwitching =
533 mModeSwitchingType == DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
第七段。
返回 DesiredDisplayModeSpecs 对象。
java
534
535 return new DesiredDisplayModeSpecs(baseMode.getModeId(),
536 allowGroupSwitching,
537 new RefreshRateRanges(
538 new RefreshRateRange(
539 primarySummary.minPhysicalRefreshRate,
540 primarySummary.maxPhysicalRefreshRate),
541 new RefreshRateRange(
542 primarySummary.minRenderFrameRate,
543 primarySummary.maxRenderFrameRate)),
544 new RefreshRateRanges(
545 new RefreshRateRange(
546 appRequestSummary.minPhysicalRefreshRate,
547 appRequestSummary.maxPhysicalRefreshRate),
548 new RefreshRateRange(
549 appRequestSummary.minRenderFrameRate,
550 appRequestSummary.maxRenderFrameRate)));
551 }
552 }
4.3.6 nativeSetDesiredDisplayModeSpecs
将 Java 的 DesiredDisplayModeSpecs 对象转成 C++ 的 DisplayModeSpecs 对象。
调用 C++ 方法 SurfaceComposerClient::setDesiredDisplayModeSpecs 将 DisplayModeSpecs 应用到 SurfaceFlinger。
java
// frameworks/base/core/jni/android_view_SurfaceControl.cpp
1298 static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
1299 jobject DesiredDisplayModeSpecs) {
1300 sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
1301 if (token == nullptr) return JNI_FALSE;
1302
1303 const auto makeRanges = [env](jobject obj) {
1304 const auto makeRange = [env](jobject obj) {
1305 gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange range;
1306 range.min = env->GetFloatField(obj, gRefreshRateRangeClassInfo.min);
1307 range.max = env->GetFloatField(obj, gRefreshRateRangeClassInfo.max);
1308 return range;
1309 };
1310
1311 gui::DisplayModeSpecs::RefreshRateRanges ranges;
1312 ranges.physical = makeRange(env->GetObjectField(obj, gRefreshRateRangesClassInfo.physical));
1313 ranges.render = makeRange(env->GetObjectField(obj, gRefreshRateRangesClassInfo.render));
1314 return ranges;
1315 };
1316
// (1)
1317 gui::DisplayModeSpecs specs;
1318 specs.defaultMode = env->GetIntField(DesiredDisplayModeSpecs,
1319 gDesiredDisplayModeSpecsClassInfo.defaultMode);
1320 specs.allowGroupSwitching =
1321 env->GetBooleanField(DesiredDisplayModeSpecs,
1322 gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching);
1323
// (2)
1324 specs.primaryRanges =
1325 makeRanges(env->GetObjectField(DesiredDisplayModeSpecs,
1326 gDesiredDisplayModeSpecsClassInfo.primaryRanges));
// (3)
1327 specs.appRequestRanges =
1328 makeRanges(env->GetObjectField(DesiredDisplayModeSpecs,
1329 gDesiredDisplayModeSpecsClassInfo.appRequestRanges));
1330
// (4)
1331 size_t result = SurfaceComposerClient::setDesiredDisplayModeSpecs(token, specs);
1332 return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
1333 }
可以看到,SurfaceFlinger 接收的 DisplayModeSpecs 设置实际上只包含几个信息:
-
默认的显示模式(即 framework 逻辑中的 base mode)
-
Primary Range(主屏幕的刷新率范围,即 framework 逻辑中的 primarySummary 指定的 "屏幕刷新率" 范围和 "应用刷新率" 范围)
-
APP Request Range(应用请求的刷新率范围,即 framework 逻辑中的 appRequestRanges 指定的 "屏幕刷新率" 范围和 "应用刷新率" 范围)
4.3.7 mode 与 VoteSummary 匹配规则
即 filterModes 方法。
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
570 private ArrayList<Display.Mode> filterModes(Display.Mode[] supportedModes,
571 VoteSummary summary) {
// 目标 VoteSummary 无效。
// 即 VoteSummary 的目标刷新率范围的最小值比最大值还要大,这不是一个合理的范围。
// 允许有 0.01(FLOAT_TOLERANCE)的误差
572 if (summary.minRenderFrameRate > summary.maxRenderFrameRate + FLOAT_TOLERANCE) {
573 if (mLoggingEnabled) {
574 Slog.w(TAG, "Vote summary resulted in empty set (invalid frame rate range)"
575 + ": minRenderFrameRate=" + summary.minRenderFrameRate
576 + ", maxRenderFrameRate=" + summary.maxRenderFrameRate);
577 }
578 return new ArrayList<>();
579 }
580
581 ArrayList<Display.Mode> availableModes = new ArrayList<>();
// 如果应用设置了期望的 base mode 刷新率,则先将 missingBaseModeRefreshRate 变量置为 true
582 boolean missingBaseModeRefreshRate = summary.appRequestBaseModeRefreshRate > 0f;
// 遍历显示设备支持的所有 modes
583 for (Display.Mode mode : supportedModes) {
// 如果该 mode 的分辨率与目标 VoteSummary 的要求不符,则忽略该 mode
584 if (mode.getPhysicalWidth() != summary.width
585 || mode.getPhysicalHeight() != summary.height) {
586 if (mLoggingEnabled) {
587 Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
588 + ": desiredWidth=" + summary.width
589 + ": desiredHeight=" + summary.height
590 + ": actualWidth=" + mode.getPhysicalWidth()
591 + ": actualHeight=" + mode.getPhysicalHeight());
592 }
593 continue;
594 }
// 如果该 mode 的刷新率不在目标 VoteSummary 的屏幕刷新率范围内,则忽略该 mode
// 允许 0.01 的误差
595 final float physicalRefreshRate = mode.getRefreshRate();
596 // Some refresh rates are calculated based on frame timings, so they aren't *exactly*
597 // equal to expected refresh rate. Given that, we apply a bit of tolerance to this
598 // comparison.
599 if (physicalRefreshRate < (summary.minPhysicalRefreshRate - FLOAT_TOLERANCE)
600 || physicalRefreshRate > (summary.maxPhysicalRefreshRate + FLOAT_TOLERANCE)) {
601 if (mLoggingEnabled) {
602 Slog.w(TAG, "Discarding mode " + mode.getModeId()
603 + ", outside refresh rate bounds"
604 + ": minPhysicalRefreshRate=" + summary.minPhysicalRefreshRate
605 + ", maxPhysicalRefreshRate=" + summary.maxPhysicalRefreshRate
606 + ", modeRefreshRate=" + physicalRefreshRate);
607 }
608 continue;
609 }
610
611 // The physical refresh rate must be in the render frame rate range, unless
612 // frame rate override is supported.
// 如果不支持 mSupportsFrameRateOverride 功能,
// 那么该 mode 的刷新率还应该在目标 VoteSummary 的渲染帧率范围内,否则忽略该 mode
// 允许 0.01 的误差
613 if (!mSupportsFrameRateOverride) {
614 if (physicalRefreshRate < (summary.minRenderFrameRate - FLOAT_TOLERANCE)
615 || physicalRefreshRate > (summary.maxRenderFrameRate + FLOAT_TOLERANCE)) {
616 if (mLoggingEnabled) {
617 Slog.w(TAG, "Discarding mode " + mode.getModeId()
618 + ", outside render rate bounds"
619 + ": minPhysicalRefreshRate=" + summary.minPhysicalRefreshRate
620 + ", maxPhysicalRefreshRate=" + summary.maxPhysicalRefreshRate
621 + ", modeRefreshRate=" + physicalRefreshRate);
622 }
623 continue;
624 }
625 }
626
// 检查该 mode 的刷新率是否能够满足目标 VoteSummary 的渲染帧率要求,如果不满足则忽略该 mode
627 if (!isRenderRateAchievable(physicalRefreshRate, summary)) {
628 if (mLoggingEnabled) {
629 Slog.w(TAG, "Discarding mode " + mode.getModeId()
630 + ", outside frame rate bounds"
631 + ": minRenderFrameRate=" + summary.minRenderFrameRate
632 + ", maxRenderFrameRate=" + summary.maxRenderFrameRate
633 + ", modePhysicalRefreshRate=" + physicalRefreshRate);
634 }
635 continue;
636 }
637
638 availableModes.add(mode);
// 如果该 mode 的刷新率跟应用设置的期望 base mode 刷新率相同(误差在 0.01 内),
// 则将 missingBaseModeRefreshRate 设置为 false。
// missingBaseModeRefreshRate 为 false 的意思是:
// -- 要么没有应用设置期望 base mode 的刷新率
// -- 要么我们找到的符合 "目标屏幕刷新率" 和 "目标渲染帧率" 要求的 modes 中,至少有一个 mode 的刷新率是跟应用设置的期望 base mode 刷新率相等的
639 if (equalsWithinFloatTolerance(mode.getRefreshRate(),
640 summary.appRequestBaseModeRefreshRate)) {
641 missingBaseModeRefreshRate = false;
642 }
643 }
// 如果符合 "目标屏幕刷新率" 和 "目标渲染帧率" 要求的 modes 的刷新率都跟应用设置的期望 base mode 刷新率不同,
// 则直接返回一个空列表
644 if (missingBaseModeRefreshRate) {
645 return new ArrayList<>();
646 }
647
// 返回符合 "目标屏幕刷新率" 和 "目标渲染帧率" 要求的 modes 列表
648 return availableModes;
649 }
650
检查目标 mode 的刷新率是否能够满足目标 VoteSummary 的渲染帧率要求:
Math.ceil() 向上取整,返回大于或等于函数参数的数值。
即对 "目标 mode 的刷新率 / 目标渲染帧率范围的最大值 - 0.01 " 向上取整。
divisor 即表示 "目标 mode 的刷新率" 是 "目标渲染帧率范围的最大值" 的多少倍。
isRenderRateAchievable 即检查目标 mode 的刷新率是否能够满足目标 VoteSummary 的渲染帧率要求。
例如,假设 VoteSummary 的渲染帧率要求范围是 [50, 70]
-
如果目标 mode 的刷新率是 120,那么平台的渲染帧率可以调整为 60,就能满足 VoteSummary 的渲染帧率要求
-
如果目标 mode 的刷新率是 90,那么平台的渲染帧率只能是90、45、30...,不能满足 VoteSummary 的渲染帧率要求
java
// frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
554 private boolean isRenderRateAchievable(float physicalRefreshRate, VoteSummary summary) {
555 // Check whether the render frame rate range is achievable by the mode's physical
556 // refresh rate, meaning that if a divisor of the physical refresh rate is in range
557 // of the render frame rate.
558 // For example for the render frame rate [50, 70]:
559 // - 120Hz is in range as we can render at 60hz by skipping every other frame,
560 // which is within the render rate range
561 // - 90hz is not in range as none of the even divisors (i.e. 90, 45, 30)
562 // fall within the acceptable render range.
563 final int divisor =
564 (int) Math.ceil((physicalRefreshRate / summary.maxRenderFrameRate)
565 - FLOAT_TOLERANCE);
566 float adjustedPhysicalRefreshRate = physicalRefreshRate / divisor;
567 return adjustedPhysicalRefreshRate >= (summary.minRenderFrameRate - FLOAT_TOLERANCE);
568 }
5. 用户设置界面
最新版本的 Android "设置" 模块中,添加了一个自动提升刷新率的功能(高刷)。
该功能就是通过修改 system settings "peak_refresh_rate" 设置高刷的。
设置菜单配置
XML
// packages/apps/Settings/res/xml/display_settings.xml
17 <PreferenceScreen
18 xmlns:android="http://schemas.android.com/apk/res/android"
19 xmlns:settings="http://schemas.android.com/apk/res-auto"
20 android:key="display_settings_screen"
21 android:title="@string/display_settings"
22 settings:keywords="@string/keywords_display">
...
97 <PreferenceCategory
98 android:title="@string/category_name_display_controls">
99
...
133
134 <SwitchPreference
135 android:key="peak_refresh_rate"
136 android:title="@string/peak_refresh_rate_title"
137 android:summary="@string/peak_refresh_rate_summary"
138 settings:controller="com.android.settings.display.PeakRefreshRatePreferenceController"/>
...
菜单标题是 "流畅画面";
菜单描述是 "自动将某些内容的刷新率从 60 Hz 调高到 %d Hz。但会增加耗电量。"
XML
<string name="peak_refresh_rate_title" msgid="1878771412897140903">"流畅画面"</string>
<string name="peak_refresh_rate_summary" msgid="3627278682437562787">"自动将某些内容的刷新率从 60 Hz 调高到 <xliff:g id="ID_1">%1$s</xliff:g> Hz。但会增加耗电量。"</string>
代码
- 功能是否使能(菜单是否显示)
功能使能的条件是
-
设置模块的 config.xml 配置 R.bool.config_show_smooth_display 为 true
-
平台支持的最大屏幕刷新率(mPeakRefreshRate)超过60(DEFAULT_REFRESH_RATE)
如果平台支持的最大屏幕刷新率都不超过 60,提高刷新率是不可能的。
java
// packages/apps/Settings/src/com/android/settings/display/PeakRefreshRatePreferenceController.java
98 @Override
99 public int getAvailabilityStatus() {
100 if (mContext.getResources().getBoolean(R.bool.config_show_smooth_display)) {
101 return mPeakRefreshRate > DEFAULT_REFRESH_RATE ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
102 } else {
103 return UNSUPPORTED_ON_DEVICE;
104 }
105 }
- 使能方法
设置 system settings "peak_refresh_rate" 的值为平台支持的最大屏幕刷新率。
java
// packages/apps/Settings/src/com/android/settings/display/PeakRefreshRatePreferenceController.java
39 public class PeakRefreshRatePreferenceController extends TogglePreferenceController
40 implements LifecycleObserver, OnStart, OnStop {
...
107 @Override
108 public boolean isChecked() {
109 final float peakRefreshRate =
110 Settings.System.getFloat(
111 mContext.getContentResolver(),
112 Settings.System.PEAK_REFRESH_RATE,
113 getDefaultPeakRefreshRate());
114 return Math.round(peakRefreshRate) == Math.round(mPeakRefreshRate);
115 }
117 @Override
118 public boolean setChecked(boolean isChecked) {
119 final float peakRefreshRate = isChecked ? mPeakRefreshRate : DEFAULT_REFRESH_RATE;
120 Log.d(TAG, "setChecked to : " + peakRefreshRate);
121
122 return Settings.System.putFloat(
123 mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE, peakRefreshRate);
124 }
6. 默认配置
在小米 pad 和 oppo findx3 上,device_config <"display_manager", "peak_refresh_rate_default">未设置,因此默认的刷新率由 com.android.internal.R.integer.config_defaultPeakRefreshRate 设置。
bash
255|yudi:/ $ device_config get display_manager peak_refresh_rate_default
null
只要修改 com.android.internal.R.integer.config_defaultPeakRefreshRate 配置即可设置默认刷新率。例如添加下面的内容,设置默认刷新率为 60。
XML
// frameworks/base/core/res/res/values/config.xm
<!-- The default peak refresh rate for a given device. -->
<integer name="config_defaultPeakRefreshRate">60</integer>