Android DisplayManagerService--0
本文基于Android 11.0源码分析
1.概述
Android中调节亮度有如下几种常见方式:
-
手动亮度条调节;
-
视频播放界面滑动调节;
-
自动亮度调节;
-
其中前两类属于手动调节,亮度调节过程中,亮度的来源和入口各不相同,但是最终都会进入DisplayPowerController中确定最终要设置的亮度。下面分别来看它们的流程。
2.手动亮度调节
手动亮度调节是指通过拖动亮度条实现的亮度调节,亮度条对应的控件位于SystemUI模块中:
frameworks/base/packages/SystemUI/src/com/android/systemui/settings/ToggleSliderView.java
frameworks/base/packages/SystemUI/src/com/android/systemui/settings/ToggleSeekBar.java
2.1 滑动事件响应
当亮度条拖动后,对应ToggleSeekBar中以下方法会发生回调:
// frameworks/base/packages/SystemUI/src/com/android/systemui/settings/ToggleSliderView.java
private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// 交给mListener处理
if (mListener != null) {
mListener.onChanged(
ToggleSliderView.this, mTracking, mToggle.isChecked(), progress, false);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// 开始拖动
mTracking = true;
if (mListener != null) {
mListener.onChanged(ToggleSliderView.this, mTracking, mToggle.isChecked(),
mSlider.getProgress(), false);
}
mToggle.setChecked(false);
......
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// 手指松开
mTracking = false;
if (mListener != null) {
mListener.onChanged(ToggleSliderView.this, mTracking, mToggle.isChecked(),
mSlider.getProgress(), true);
}
......
}
};
这里将回调事件交给mListener进行处理,该对象是BrightnessController的实例,所以接下来就会在BrightnessController#onChanged()方法中对滑动事件做进一步处理:
// frameworks/base/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
public void onChanged(ToggleSlider toggleSlider, boolean tracking, boolean automatic,
int value, boolean stopTracking) {
......
final float minBacklight;
final float maxBacklight;
final int metric;
final String settingToChange;
if (mIsVrModeEnabled) {
......
} else {
metric = mAutomatic
? MetricsEvent.ACTION_BRIGHTNESS_AUTO
: MetricsEvent.ACTION_BRIGHTNESS;
minBacklight = mMinimumBacklight;
maxBacklight = mMaximumBacklight;
settingToChange = Settings.System.SCREEN_BRIGHTNESS_FLOAT;
}
final float valFloat = MathUtils.min(convertGammaToLinearFloat(value,
minBacklight, maxBacklight),
1.0f);
// 设置亮度值
setBrightness(valFloat);
// 当手指抬起后,向SettingsProvider中写入亮度值
if (!tracking) {
AsyncTask.execute(new Runnable() {
public void run() {
Settings.System.putFloatForUser(mContext.getContentResolver(),
settingToChange, valFloat, UserHandle.USER_CURRENT);
}
});
}
......
}
以上方法中:
-
首先,通过convertGammaToLinear()方法将ToggleSeekBar进度值转换为对应的亮度值;
-
然后,通过DisplayManager#setTemporaryBrightness()方法向FW中不断进行亮度设置;
-
最后,当用户抬起手指后,通过异步任务更新SettingsProvider中的亮度值。
从Android R开始,AOSP进行了基于浮点类型的亮度设置,因此,在设置过程中,得到的亮度始终是一个[0.0f, 1.0f]之间的浮点值,相关部分在下面讲到。
在滑动过程中,会不断使用setBrightness()方法向DMS中下发亮度进行设置,并在滑动结束后,更新SettingsProvider中的亮度字段Settings.System.SCREEN_BRIGHTNESS_FLOAT的值。
setBrightness()方法中,直接调用DMS#setTemporaryBrightness()方法:
// frameworks/base/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
private void setBrightness(float brightness) {
mDisplayManager.setTemporaryBrightness(brightness);
}
执行到这里,对滑动事件响应完成,下面看下DMS中收到亮度值的处理。
2.2 DMS#setTemporaryBrightness()设置亮度
DMS#setTemporaryBrightness()方法如下:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
@Override // Binder call
public void setTemporaryBrightness(float brightness) {
......
synchronized (mSyncRoot) {
mDisplayPowerController.setTemporaryBrightness(brightness);
}
}
这里直接交给了DisplayPowerController去处理。DisplayPowerController中通过Handler在"PowerManagerService"线程继续执行:
// frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private final class DisplayControllerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
......
case MSG_SET_TEMPORARY_BRIGHTNESS:
// 得到来自SystemUI的亮度值
mTemporaryScreenBrightness = Float.intBitsToFloat(msg.arg1);
// 更新状态
updatePowerState();
break;
......
}
}
}
这里将来自SystemUI的浮点亮度值赋值给了mTemporaryScreenBrightness变量,之所以以temporary命名,是因为一旦确定亮度,该变量值就会被重置,这个值的作用也是相当重要,如果该值没有发生重置,自动亮度将无法设置。
之后开始调用updatePowerState()更新display整体状态,并将mTemporaryScreenBrightness作为为系统的亮度进行设置:
// frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private void updatePowerState() {
......
// 用来更新用户状态栏、设置中手动设置的亮度值的状态,如果用户设置亮度发生变化,返回true
final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
// 该亮度表示用户拖动亮度条调节且未放手时的亮度,所以是一个"临时"亮度,如果存在这个值,则必须使用这个值
if (isValidBrightnessValue(mTemporaryScreenBrightness)) {
brightnessState = mTemporaryScreenBrightness;
// 表示使用了临时用户亮度
mAppliedTemporaryBrightness = true;
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_TEMPORARY);
} else {
mAppliedTemporaryBrightness = false;
}
......
}
DisplayPowerController#updatePowerState()的详细分析见《Android DisplayManagerService--03:DMS部分亮灭屏流程》。
3.视频界面亮度调节
视频界面调节亮度这种场景大家都不会陌生,这种方式的实现,是通过给窗口设置属性来实现的。当给窗口设置WindowManger.LayoutParams.screenBrightnesss属性后,进入该窗口就会调节亮度。
在WMS模块中进行窗口遍历时,会对该属性进行判断:
// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
boolean handleNotObscuredLocked(WindowState w, boolean obscured, boolean syswin) {
final WindowManager.LayoutParams attrs = w.mAttrs;
final int attrFlags = attrs.flags;
final boolean onScreen = w.isOnScreen();
final boolean canBeSeen = w.isDisplayedLw();
......
if (w.mHasSurface && canBeSeen) {
......
if (!syswin && w.mAttrs.screenBrightness >= 0
&& Float.isNaN(mScreenBrightnessOverride)) {
mScreenBrightnessOverride = w.mAttrs.screenBrightness;
}
......
}
return displayHasContent;
}
然后在执行完窗口Surface的放置工作后,将此亮度设置给PMS:
// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
void performSurfacePlacementNoTrace() {
......
if (!mWmService.mDisplayFrozen) {
// 进行有效性判断
final float brightnessOverride = mScreenBrightnessOverride < PowerManager.BRIGHTNESS_MIN
|| mScreenBrightnessOverride > PowerManager.BRIGHTNESS_MAX
? PowerManager.BRIGHTNESS_INVALID_FLOAT : mScreenBrightnessOverride;
int brightnessFloatAsIntBits = Float.floatToIntBits(brightnessOverride);
mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, brightnessFloatAsIntBits,
0).sendToTarget();
}
}
通过PMS#setScreenBrightnessOverrideFromWindowManager()方法将亮度发送给PMS:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void setScreenBrightnessOverrideFromWindowManagerInternal(float brightness) {
synchronized (mLock) {
if (!BrightnessSynchronizer.floatEquals(mScreenBrightnessOverrideFromWindowManager,
brightness)) {
// 设置给全局变量mScreenBrightnessOverrideFromWindowManager
mScreenBrightnessOverrideFromWindowManager = brightness;
mDirty |= DIRTY_SETTINGS;
updatePowerStateLocked();
}
}
}
PMS中收到后,会将该亮度作为DisplayPowerRequest.screenBrightnessOverride属性的值数,向DMS模块发起请求, 在DisplayPowerController中处理请求时,则将该亮度设置为全局亮度。
// frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private void updatePowerState() {
......
// 使用PMS中的覆盖亮度
if ((Float.isNaN(brightnessState))
&& isValidBrightnessValue(mPowerRequest.screenBrightnessOverride)) {
brightnessState = mPowerRequest.screenBrightnessOverride;
// 记录亮度变化原因
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_OVERRIDE);
// 表示使用了覆盖亮度
mAppliedScreenBrightnessOverride = true;
} else {
mAppliedScreenBrightnessOverride = false;
}
......
}
从DisplayPowerController#updatePowerState()方法中亮度的设置顺序来看,来自窗口的覆盖亮度是优先级最高的。
4.Android R浮点化的亮度设置
在Android R上,亮度最大的区别是对其进行了Float化,将以前由0~255的原始亮度区间,全部更改为0.0~1.0f。但同时为了兼容旧方案,也会对现有的Int型亮度转换为对应的Float型亮度,按照原来的Int值参数Float化为[0.0f, 1.0f]区间内的值。
4.1 新添加配置参数
在Android R上,新增如下几个配置值:
<!-- frameworks/base/core/res/res/values/config.xml -->
<!-- 最小亮度 -->
<item name="config_screenBrightnessSettingMinimumFloat" format="float" type="dimen">-2</item>
<!-- 最大亮度 -->
<item name="config_screenBrightnessSettingMaximumFloat" format="float" type="dimen">-2</item>
<!-- 默认亮度 -->
<item name="config_screenBrightnessSettingDefaultFloat" format="float" type="dimen">-2</item>
<!-- doze默认亮度 -->
<item name="config_screenBrightnessDozeFloat" format="float" type="dimen">0.0</item>
<!-- dim亮度 -->
<item name="config_screenBrightnessDimFloat" format="float" type="dimen">0.05</item>
这几个值分别对应了Q版本上同性质的配置:
<!-- frameworks/base/core/res/res/values/config.xml -->
<integer name="config_screenBrightnessSettingMinimum">10</integer>
<integer name="config_screenBrightnessSettingMaximum">255</integer>
<integer name="config_screenBrightnessSettingDefault">102</integer>
<integer name="config_screenBrightnessDoze">1</integer>
<integer name="config_screenBrightnessDim">10</integer>
因此在R上,配置应优先以Float类型参数进行,如果没有进行Float类型参数配置,会将Int型配置参数转换为Float型配置。
4.2 旧方案(Int)和新方案(Float)间的转换
在PowerManagerService中,会读取以上新增配置参数:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
@VisibleForTesting
PowerManagerService(Context context, Injector injector) {
.........
// 读取Float型配置参数
final float min = mContext.getResources().getFloat(com.android.internal.R.dimen
.config_screenBrightnessSettingMinimumFloat);
final float max = mContext.getResources().getFloat(com.android.internal.R.dimen
.config_screenBrightnessSettingMaximumFloat);
final float def = mContext.getResources().getFloat(com.android.internal.R.dimen
.config_screenBrightnessSettingDefaultFloat);
final float doze = mContext.getResources().getFloat(com.android.internal.R.dimen
.config_screenBrightnessDozeFloat);
final float dim = mContext.getResources().getFloat(com.android.internal.R.dimen
.config_screenBrightnessDimFloat);
// 进行Int -> Float的转换
if (min == INVALID_BRIGHTNESS_IN_CONFIG || max == INVALID_BRIGHTNESS_IN_CONFIG
|| def == INVALID_BRIGHTNESS_IN_CONFIG) {
// 最小亮度的转换
mScreenBrightnessMinimum = BrightnessSynchronizer.brightnessIntToFloat(
mContext.getResources().getInteger(com.android.internal.R.integer
.config_screenBrightnessSettingMinimum),
PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
// 最大亮度的转换
mScreenBrightnessMaximum = BrightnessSynchronizer.brightnessIntToFloat(
mContext.getResources().getInteger(com.android.internal.R.integer
.config_screenBrightnessSettingMaximum),
PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
// 默认亮度的转换
mScreenBrightnessDefault = BrightnessSynchronizer.brightnessIntToFloat(
mContext.getResources().getInteger(com.android.internal.R.integer
.config_screenBrightnessSettingDefault),
PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
} else {
mScreenBrightnessMinimum = min;
mScreenBrightnessMaximum = max;
mScreenBrightnessDefault = def;
}
......
}
这里看到,首先会读取Float参数配置,如果min、max、def均存在有效配置,则直接使用,这里定义如果值为-2则表示无效配置,这个值也是AOSP默认的配置值:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f;
否则将会进行Int --> Float的转换,将旧方案中配置的Int类型参数转换为Float类型参数。
BrightnessSynchronizer类专门用来进行对Int和Float两类亮度配置参数的相互转换。比如Int转Float时,通过BrightnessSynchronizer.brightnessIntToFloat()方法进行。
4.3 新增API
Android R上,尽管保留了PowerManager中原来获取亮度的API,如getMinimumScreenBrightnessSetting()等接口,但几乎找不到它们的使用。取而代之的是,新增PowerManager#getBrightnessConstraint(int)接口,其他模块对于亮度配置值的获取,将全部通过此接口获取,并通过参数进行限定。参数的定义也在PowerManager中:
// frameworks/base/core/java/android/os/PowerManager.java
public static final int BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM = 0; // 最小亮度
public static final int BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM = 1; // 最大亮度
public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT = 2; // 默认亮度
public static final int BRIGHTNESS_CONSTRAINT_TYPE_DIM = 3; // DIM亮度
public static final int BRIGHTNESS_CONSTRAINT_TYPE_DOZE = 4; // Doze默认亮度
public static final int BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR = 5; // VR模式最小亮度
public static final int BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR = 6; // VR模式最大亮度
public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR = 7; // VR模式默认亮度
除此之外,在PowerManager中,还新添加了几个常量限定值:
// frameworks/base/core/java/android/os/PowerManager.java
// Q上已定义
public static final int BRIGHTNESS_ON = 255;
public static final int BRIGHTNESS_OFF = 0;
public static final int BRIGHTNESS_DEFAULT = -1;
// R新增常量
public static final int BRIGHTNESS_INVALID = -1;
public static final float BRIGHTNESS_MAX = 1.0f;
public static final float BRIGHTNESS_MIN = 0.0f;
public static final float BRIGHTNESS_OFF_FLOAT = -1.0f;
public static final float BRIGHTNESS_INVALID_FLOAT = Float.NaN;
PowerManager#getBrightnessConstraint(int)方法如下:
// frameworks/base/core/java/android/os/PowerManager.java
public float getBrightnessConstraint(int constraint) {
try {
return mService.getBrightnessConstraint(constraint);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
此方法将通过Binder直接调用到PMS中:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
public float getBrightnessConstraint(int constraint) {
switch (constraint) {
case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM:
return mScreenBrightnessMinimum;
case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM:
return mScreenBrightnessMaximum;
case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT:
return mScreenBrightnessDefault;
case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM:
return mScreenBrightnessDim;
case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE:
return mScreenBrightnessDoze;
case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR:
return mScreenBrightnessMinimumVr;
case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR:
return mScreenBrightnessMaximumVr;
case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR:
return mScreenBrightnessDefaultVr;
default:
return PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
}
这里举个实例,在状态栏中,就通过此接口获取最大最小亮度,并对亮度进行限制:
// frameworks/base/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
// 获取亮度最大最小值
mMinimumBacklight = pm.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
mMaximumBacklight = pm.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
// 根据进度条值计算出float亮度值
final float valFloat = MathUtils.min(convertGammaToLinearFloat(value,
minBacklight, maxBacklight), 1.0f);
// 设置亮度
private void setBrightness(float brightness) {
mDisplayManager.setTemporaryBrightness(brightness);
}
4.4 内部接口Float
既然参数全部Float化了,那么原来方案中涉及到相关参数的方案,也全部需要对参数进行float化,比如上面的DisplayManager#setTemporaryBrightness(float)方法,涉及到framework内部的类如DisplayPowerController、DisplayPowerState、LocalDisplayAdapter、AutomaticBrighnessController等,都将原方法中涉及的int参数转换为了Float。这里就不一一列举了。
4.5 AutomaticBrightnessController中的变化
自动亮度方面变化不是很多,因为在R之前,自动亮度就已经实现了Float化,不管是配置曲线还是自动亮度的获取,都是Float类型,只不过在传递给DisplayPowerController时,进了Int转换。R上则去掉了这个转换,直接进行使用:
// frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private void updateAutoBrightness(boolean sendUpdate, boolean isManuallySet) {
if (!mAmbientLuxValid) {
return;
}
// 这里获取得到[0.0, 1.0]区间的亮度
float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
mForegroundAppCategory);
// 不再进行转化,直接设置float
float newScreenAutoBrightness = clampScreenBrightness(value);
if (!BrightnessSynchronizer.floatEquals(mScreenAutoBrightness,
newScreenAutoBrightness)) {
.......
if (sendUpdate) {
mCallbacks.updateBrightness();
}
}
}
4.6 SettingsProvider中新添加字段
Android R以前,亮度值设置后,是保存在Settings.System.SCREEN_BRIGHTNESS字段中的,在R上,这个字段也进行了保留,但由于Float化,因此还添加了对应的Float值的字段Settings.System.SCREEN_BRIGHTNESS_FLOAT,亮度发生变化后,首先是更新该字段:
// frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private void putScreenBrightnessSetting(float brightnessValue) {
mCurrentScreenBrightnessSetting = brightnessValue;
Settings.System.putFloatForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessValue, UserHandle.USER_CURRENT);
}
而为了对两个字段的数据保持一致性,当该字段值发生变化后,也会对旧字段进行对应的更新,这个也是在BrightnessSynchronizer中进行的:
// frameworks/base/core/java/com/android/internal/BrightnessSynchronizer.java
private void updateBrightnessIntFromFloat(float value) {
int newBrightnessInt = brightnessFloatToInt(mContext, value);
Object topOfQueue = mWriteHistory.peek();
if (topOfQueue != null && topOfQueue.equals(value)) {
mWriteHistory.poll();
} else {
mWriteHistory.offer(newBrightnessInt);
mPreferredSettingValue = value;
Settings.System.putIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, newBrightnessInt, UserHandle.USER_CURRENT);
}
}
同理,如果int字段值发生变化,新字段中的值也会同样进行变化,从而更新亮度。