Android 性能之刷新率设置和管理

Android 性能之刷新率设置和管理对于 Android 平台来说 屏幕的刷新率会影响应用绘制的调度周期

大家好,欢迎来到IT知识分享网。

目录

1. 刷新率和帧率

2. 多种刷新率

3. 基本原理

3.1 屏幕 & 显示控制器

3.2 Composer Service

4. Framework 策略

4.1基本架构

4.2 刷新率设置项的定义

4.2.1 最低刷新率

4.2.2 默认刷新率 & 默认的用户设置刷新率

4.2.2.1 设置入口

4.2.2.2 设置场景

4.2.2.3 DisplayDeviceConfig

4.2.3 用户设置的刷新率

4.2.4 应用刷新率 & 应用期望的 base mode 刷新率

4.3 刷新率更新 & 应用流程

4.3.1 入口

4.3.1.1 system settings “peak_refresh_rate”

4.3.1.2 device_config <“display_manager”, “peak_refresh_rate_default”>

4.3.1.3 setDefaultRefreshRate 方法设置 “默认刷新率”(Just for test)

4.3.1.4 窗口切换

4.3.1.5 小结

4.3.2 Vote & VotesStorage

4.3.2.1 Vote

4.3.2.2 VotesStorage

4.3.3 DisplayModeDirector.updateRefreshRateSettingLocked

4.3.4 DisplayModeDirector .notifyDesiredDisplayModeSpecsChangedLocked

4.3.5 DisplayModeDirector.getDesiredDisplayModeSpecs

4.3.6 nativeSetDesiredDisplayModeSpecs

4.3.7 mode 与 VoteSummary 匹配规则

5. 用户设置界面

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):应用绘制的调度频率,是平台软件属性
  • 刷新率:包含屏幕刷新率和应用刷新率

需要注意的是,设置应用刷新率时,必须保证屏幕刷新率能被应用刷新率整除。

这是因为屏幕刷新率不能被应用刷新率整除时,帧的间隔将不均匀;而眼睛和大脑期望看到的是平滑、连续的运动,当帧显示时间不均匀时,会产生跳动感,即抖动。

Android 性能之刷新率设置和管理
 

一般,如果屏幕刷新率能被应用刷新率整除,我们就称应用刷新率与屏幕刷新率同步(而非完全相同)。

3. 基本原理

刷新率设置流程,涉及

  • 硬件,即屏幕和显示控制器
  • Hal Service,即 composer servcie
  • SurfaceFlinger
  • Framework,即系统服务
  • 应用

数据流向从下至上。

3.1 屏幕 & 显示控制器

显示控制器(Display Controller)是一个连接屏幕的硬件单元,负责管理和驱动屏幕显示图像的各个方面。它通常包括以下功能:

  1. 帧缓冲管理:显示控制器从帧缓冲区读取图像数据,这些数据是由图形处理单元(GPU)或中央处理单元(CPU)生成并存储的。
  2. 信号生成:显示控制器生成合适的信号来驱动屏幕。这些信号包括水平和垂直同步信号、时钟信号以及数据传输信号。
  3. 分辨率和刷新率:控制器管理屏幕的分辨率和刷新率,确保图像以正确的分辨率显示,并以适当的刷新率更新屏幕。
  4. 颜色空间转换:显示控制器可能需要将图像数据从一种颜色空间转换到另一种,以适应不同的显示要求。
  5. 图层合成:在一些高级显示控制器中,可以支持多图层合成。这意味着它可以将来自不同来源的多个图像合成到一个最终显示的图像上。这对于窗口系统、叠加窗口以及复杂的用户界面来说非常重要。

显示控制器的工作流程

  1. 接收数据:显示控制器从系统内存或专用的图形内存中读取图像数据。
  2. 处理数据:如果需要,显示控制器会对数据进行处理,例如颜色空间转换、缩放、旋转等。
  3. 输出信号:将处理过的数据转换为显示屏能够理解的信号格式,并通过显示接口(如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。

:/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 :/system_ext/etc/rpms # ps -A|grep 1387 system 1387 1  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:提供了与显示控制器交互的标准接口,包括
    • 发送帧
    • 设置显示模式
  • 显示控制器:硬件单元,负责接收图像数据,并将其显示在屏幕上。

显示的工作流程

  1. SurfaceFlinger 合成帧:SurfaceFlinger 将来自不同应用的图形内容合成为最终的显示帧。
  2. 通过 composer service 发送帧:SurfaceFlinger 通过 composer service 接口将合成的帧发送到显示控制器。
  3. 显示控制器处理帧:显示控制器接收帧数据,并生成必要的信号将其显示在屏幕上。

设置的工作流程

  1. SurfaceFlinger 接收来自 Framework 的显示设置,包括默认的 display mode(base mode)、屏幕刷新率范围、应用刷新率范围、分辨率等,根据这些显示设置选择显示模式
  2. 通过 composer service 设置显示模式
  3. 显示控制器应用显示模式,比如修改屏幕分辨率、刷新率等

这里顺便说明下 composer service 和硬件合成器(hardware composer 或 hwcomposer)的关系。

在 Android 系统中,SurfaceFlinger 有硬件合成和软件合成的区别。我们需要澄清 hwcomposer 和 composer service 的角色,以及硬件合成和软件合成的概念。

SurfaceFlinger 和合成方式:

  1. 软件合成:
    1. 在软件合成模式下,SurfaceFlinger 使用 GPU 或 CPU 来合成各个图层。
    2. 这种方式虽然灵活,但可能会消耗更多的 CPU/GPU 资源。
  2. 硬件合成:
    1. 在硬件合成模式下,SurfaceFlinger 利用专门的显示控制器硬件(Hardware Composer)来合成图层。
    2. 这种方式更高效,因为专用硬件可以更快地处理图层合成,并减少对 CPU/GPU 的依赖,从而提高性能和省电。

合成的工作流程:

  1. SurfaceFlinger 负责管理图层,并决定是使用硬件合成还是软件合成。
  2. 硬件合成:
    1. 如果选择硬件合成,SurfaceFlinger 通过 composer service 接口(如 android.hardware.graphics.composer@2.4)与硬件合成器进行通信,利用硬件加速功能进行图层合成,并生成最终的显示帧。
  3. 软件合成:
    1. 如果选择软件合成,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 之间的中间层。

Android 性能之刷新率设置和管理

本文主要目的是介绍 Framework 的刷新率设置策略,不关注显示控制器的硬件实现和 composer service 以及 SurfaceFlinger 的实现细节。

(SurfaceFlinger 内部的实现细节以后有时间再专门介绍)

不过要理解 Framework 的刷新率设置策略,有必要先了解 SurfaceFlinger 设置 display mode 和应用刷新率的基本逻辑。

有 3 个参数会影响 SurfaceFlinger 设置 display mode 和应用刷新率

  1. base mode(default mode):表示默认的 display mode,即如果没有合适的 display mode,就会选择 base mode
  2. primary range:表示屏幕刷新率范围,即目标 display mode 的屏幕刷新率应该在这个范围内
  3. 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 刷新率策略的核心代码包括

  1. frameworks/base/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
  2. frameworks/base/services/core/java/com/android/server/display/mode/Vote.java
  3. 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 设置 “默认的用户设置刷新率”
// 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。

// 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 启动变慢。

// 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
// 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 }
// 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) 方法。

// 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) 方法。

// 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 方法。
// 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 设置。

// 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 }
// 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 }
// 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)。

// 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 }
// 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 方法),因此我们需要重点关心。

// 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 表示端口号。

// 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 }
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 }
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 }
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)都没有配置默认刷新率、默认的用户设置刷新率,则使用缺省值。

// 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。

:/product # find -name "displayconfig" :/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 入口

刷新率更新 & 应用的主要入口包括

  1. 当 “默认的用户设置刷新率” 或 “用户设置的刷新率” 变化时~
    1. “默认的用户设置刷新率” 可以通过 device_config <“display_manager”, “peak_refresh_rate_default”> 修改
    2. “用户设置的刷新率” 可以通过 system settings “peak_refresh_rate” 修改
  2. 窗口切换,且上层的窗口设置了 “期望的 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 方法更新(应用)刷新率。

// 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 方法更新(应用)刷新率。

// 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 的单元测试代码中使用。

// 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 方法中

  1. 遍历所有 windows,更新 mTmpApplySurfaceChangesTransactionState。mTmpApplySurfaceChangesTransactionState 保存目标 base mode id、目标 base mode 的刷新率、”应用刷新率 [min, max]” 信息。
  2. 调用 DMS.LocalService.setDisplayProperties 方法将 base mode 和 “应用刷新率 [min, max]” 更新到 VotesStorage,VotesStorage 更新时通过 VotesStorage.mListener 回调 DisplayModeDirector.notifyDesiredDisplayModeSpecsChangedLocked 方法将 base mode 和 “应用刷新率 [min, max]” 设置到 SurfaceFlinger。
// 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

// 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 } ...
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 方法。

  1. 调用 setAppRequestedModeLocked 方法更新 “应用期望的 base mode 刷新率”
  2. 调用 setAppPreferredRefreshRateRangeLocked 方法更新 “应用刷新率” 范围
// 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(屏下指纹)相关。

这些策略优先级的定义如下,它们是常量。

// 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 刷新率。

// 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 刷新率

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>>。

// 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。

  1. updateGlobalVote 方法更新 GLOBAL_ID 即全局的 Votes map。
  2. updateVote 方法更新目标 display id 的 Votes map。
// 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 方法。

// 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 
// 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 方法。

相当于:

mVotesStorage = new VotesStorage(new Listener() {@Overridepublic void onChanged() { notifyDesiredDisplayModeSpecsChangedLocked(); } });

返回目标 display id 的 Votes map。

// 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 更新全局刷新率策略。

// 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/): 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/ 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。

// 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 方法。

// 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 方法设置。

// 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。

// 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

  1. 由 DisplayModeDirector$DesiredDisplayModeSpecs.baseModeId 得到目标 base mode id
  2. 将目标 base mode id 连同 DisplayModeDirector$DesiredDisplayModeSpecs.primary、DisplayModeDirector$DesiredDisplayModeSpecs.appRequest 一起封装到 SurfaceControl$DesiredDisplayModeSpecs 结构,并设置到 SurfaceFlinger。mSurfaceControlProxy.setDesiredDisplayModeSpecs 调用 native 方法 nativeSetDesiredDisplayModeSpecs。
// 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 的返回值。

// 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 方法的作用。

  1. 入参 SparseArray<Vote> votes 是目标 Votes map
  2. 入参 int lowestConsideredPriority 和 int highestConsideredPriority 表示目标 priority 范围
  3. 入参 VoteSummary summary 是 out 输出
// 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 方法

  1. 对 Votes 的 “屏幕刷新率” 范围取交集,成为目标 “屏幕刷新率”
  2. 对 Votes 的 “应用刷新率” 范围取交集,成为目标 “应用刷新率”
  3. 如果 Votes 包含具有 “应用期望的 base mode 刷新率” 的 Vote,将其中 priority 最高的设置为目标 “base mode 刷新率”

注意,如果目标 Votes map 中没有满足目标 priority 范围的 Vote,返回的 VoteSummary 对象的刷新率范围就是 [0,无限大]

// 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 段分析。

第一段。

// 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 为空。
// 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 的父集。

// 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 中,

  1. 选择一个刷新率与应用设置的期望 base mode 刷新率(primarySummary.appRequestBaseModeRefreshRate)相同的 mode 作为 base mode;
  2. 如果应用没有设置期望 base mode 刷新率,则选择一个与 defaultMode 拥有相同刷新率的 mode 作为 base mode
  3. 如果 availableModes 列表中没有满足条件的 mode,则选择列表中首个 mode 作为 base mode
  4. 如果 availableModes 为空,则返回 null
// 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 方法代码如下:

// 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” 有三种:

  1. mModeSwitchingType 设置为 DisplayManager.SWITCHING_TYPE_NONE(最强) 这种情况下,会调较 primarySummary 和 appRequestSummary,使 primarySummary 和 appRequestSummary 固定 “屏幕刷新率” 和 “应用刷新率”。即,屏幕刷新率和平台的渲染帧率不会自动变化
  2. mModeSwitchingType 设置为 DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY(次强) 这种情况下,会调较 primarySummary 和 appRequestSummary,使 primarySummary 固定 “屏幕刷新率” 和 “应用刷新率”,使 appRequestSummary 固定 “屏幕刷新率”。即,屏幕刷新率不会自动变化,但平台的渲染帧率可能会自动变化
  3. primarySummary.disableRefreshRateSwitching 为 true(如果 Votes map 中有一个用 forBaseModeRefreshRate 方法构造的 Vote,这些 Votes 的 summary 的 disableRefreshRateSwitching 就会是 true) 这种情况下,会调较 primarySummary,使该 primarySummary 固定 “屏幕刷新率”。即屏幕刷新率和平台的渲染帧率都可能会自动变化
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。

其做法是,

  1. 将 VoteSummary 的屏幕刷新率固定为目标 fps;
  2. 如果 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

其做法是

  1. 首先修改 VoteSummary 的渲染帧率范围最大值,修改成与最小值一样;
  2. 检查目标 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]

注:这里的渲染帧率即应用刷新率。

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”。

532 boolean allowGroupSwitching = 533 mModeSwitchingType == DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;

第七段。

返回 DesiredDisplayModeSpecs 对象。

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。

// 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 设置实际上只包含几个信息:

  1. 默认的显示模式(即 framework 逻辑中的 base mode)
  2. Primary Range(主屏幕的刷新率范围,即 framework 逻辑中的 primarySummary 指定的 “屏幕刷新率” 范围和 “应用刷新率” 范围)
  3. APP Request Range(应用请求的刷新率范围,即 framework 逻辑中的 appRequestRanges 指定的 “屏幕刷新率” 范围和 “应用刷新率” 范围)

4.3.7 mode 与 VoteSummary 匹配规则

即 filterModes 方法。

// 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 的渲染帧率要求
// 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” 设置高刷的。

设置菜单配置

// 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。但会增加耗电量。”

 <string name="peak_refresh_rate_title" msgid="">"流畅画面"</string> <string name="peak_refresh_rate_summary" msgid="">"自动将某些内容的刷新率从 60 Hz 调高到 <xliff:g id="ID_1">%1$s</xliff:g> Hz。但会增加耗电量。"</string>

代码

  • 功能是否使能(菜单是否显示)

功能使能的条件是

  1. 设置模块的 config.xml 配置 R.bool.config_show_smooth_display 为 true
  2. 平台支持的最大屏幕刷新率(mPeakRefreshRate)超过60(DEFAULT_REFRESH_RATE)

如果平台支持的最大屏幕刷新率都不超过 60,提高刷新率是不可能的。

// 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” 的值为平台支持的最大屏幕刷新率。

// 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. 默认配置

 有些设备上,device_config <“display_manager”, “peak_refresh_rate_default”> 未设置,因此默认的刷新率由 com.android.internal.R.integer.config_defaultPeakRefreshRate 设置。

:/ $ device_config get display_manager peak_refresh_rate_default null

只要修改 com.android.internal.R.integer.config_defaultPeakRefreshRate 配置即可设置默认刷新率。例如添加下面的内容,设置默认刷新率为 60。

// frameworks/base/core/res/res/values/config.xm <!-- The default peak refresh rate for a given device. --> <integer name="config_defaultPeakRefreshRate">60</integer>

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/156962.html

(0)
上一篇 2025-02-08 22:26
下一篇 2025-02-08 22:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信