在android 9.0中,相比android 8.1而言,背光部分逻辑有较大的调整,这里就对android P背光机制进行完整的分析。
1.手动调节亮度
1.1.在SystemUI、Settings中手动调节
在界面(SystemUI)和Settings中拖动进度条调节亮度时,调节入口在BrightnessController中:
@Override
public void onChanged(ToggleSlider toggleSlider, boolean tracking, boolean automatic,
int value, boolean stopTracking) {
final String setting;
if (mIsVrModeEnabled) {
setting = Settings.System.SCREEN_BRIGHTNESS_FOR_VR;
} else {
setting = Settings.System.SCREEN_BRIGHTNESS;
}
//获取亮度值
final int val = convertGammaToLinear(value, min, max);
//设置亮度值
setBrightness(val);
if (!tracking) {
//在异步任务中将新的亮度值保存在SettingsProvider中
AsyncTask.execute(new Runnable() {
public void run() {
Settings.System.putIntForUser(mContext.getContentResolver(),
setting, val, UserHandle.USER_CURRENT);
}
});
}
}
在BrightnessController中,首先根据亮度条的拖动,计算出新的亮度值,然后将调用本类中的setBrightneess()
设置亮度,设置完成后,通过异步任务将新的亮度值保存在SettingsProvider中,我们看下一个方法:
private void setBrightness(int brightness) {
mDisplayManager.setTemporaryBrightness(brightness);
}
在以上方法中,调用了DisplayManager对象的方法开始设置亮度,这和android8.1的一个不同点,在android 8.1中,设置亮度是由PMS开始,而在9.0中,直接从DisplayManagerService开始了。
当调用mDisplayManager的setTemporaryBrightness()
后,经过一系列调用,最终进入了DisplayPowerController·中,这些调用过程代码如下:
//frameworks/base/core/java/android/hardware/display/DisplayManager.java
public void setTemporaryBrightness(int brightness) {
mGlobal.setTemporaryBrightness(brightness);
}
//frameworks/base/core/java/android/hardware/display/DisplayManagerGlobal.java
public void setTemporaryBrightness(int brightness) {
try {
mDm.setTemporaryBrightness(brightness);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
//frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
@Override // Binder call
public void setTemporaryBrightness(int brightness) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
"Permission required to set the display's brightness");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
mDisplayPowerController.setTemporaryBrightness(brightness);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
我们直接进入DisplayPowerController中的setTemporaryBrightness()
方法:
public void setTemporaryBrightness(int brightness) {
Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS,
brightness, 0 /*unused*/);
msg.sendToTarget();
}
在这个方法中,通过Handler发送一个消息进行处理,这样做的目的是,将最终的亮度调节放在PowerManagerService线程中进行,因为这个Handler对象正是来自于PMS中。
继续下一步流程,来看看Handler中如何处理:
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SET_TEMPORARY_BRIGHTNESS:
// TODO: Should we have a a timeout for the temporary brightness?
//将brightness赋值给了mTemporaryScreenBrightness
mTemporaryScreenBrightness = msg.arg1;
updatePowerState();
break;
}
}
}
在handleMessage()中,将亮度值赋给了全局变量mTemporaryScreenBrightness ,然后开始调用updatePowerState()
方法。
关于updatePowerState()
方法,不做全部分析,这里只看亮度调节相关逻辑:
private void updatePowerState() {
// ......
//手动设置亮度是否改变
final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
if (userSetBrightnessChanged) {
mTemporaryScreenBrightness = -1;
}
// Use the temporary screen brightness if there isn't an override, either from
// WindowManager or based on the display state.
if (mTemporaryScreenBrightness > 0) {
//使用手动设置的亮度
brightness = mTemporaryScreenBrightness;
mAppliedTemporaryBrightness = true;
} else {
mAppliedTemporaryBrightness = false;
}
//........
if (!mPendingScreenOff) {
final boolean isDisplayContentVisible = mColorFadeEnabled ?
(mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0f) :
(state == Display.STATE_ON && mSkipRampState == RAMP_STATE_SKIP_NONE);
if (initialRampSkip || hasBrightnessBuckets
|| wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) {
animateScreenBrightness(brightness, 0);
} else {
animateScreenBrightness(brightness,
slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
}
//......
}
在updatePowerState()
方法中,如果此时mTemporaryScreenBrightness
大于0,则设备将使用它作为最终的亮度,而它大于0取决与updateUserSetScreenBrightness()
方法的返回值,该方法如下:
private boolean updateUserSetScreenBrightness() {
if (mPendingScreenBrightnessSetting < 0) {
return false;
}
//add for bug BEG
if (mPendingScreenBrightnessSetting > 0 && (mCurrentScreenBrightnessSetting == mTemporaryScreenBrightness)){
return true;
}
//add for bug END
if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
mPendingScreenBrightnessSetting = -1;
return false;
}
mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
mPendingScreenBrightnessSetting = -1;
return true;
}
这个方法中,实际是根据mPendingScreenBrightnessSetting
的值做不同的处理。总结来说,如果mPendingScreenBrightnessSetting
大于0,则返回true,并将当前亮度设置为它的值,否则返回false,再进一步概括,就是如果mPendingScreenBrightnessSetting
和mTemporaryScreenBrightness
的值都大于0,那么系统将使用mTemporaryScreenBrightness
的值作为亮度值。
mPendingScreenBrightnessSetting
则是通过SettingsObserver监测Settings数据库中的值,它获取如下:
private void handleSettingsChange(boolean userSwitch) {
mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
sendUpdatePowerState();
}
private int getScreenBrightnessSetting() {
final int brightness = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessDefault,
UserHandle.USER_CURRENT);
return clampAbsoluteBrightness(brightness);
}
而对SettingsProvider中的亮度值的保存正是在BrightnessController中setBrightness()
之后。
因此,对于手动背光调节,首先调用setBrightnessVal()
进入DPC后,将调节亮度设置给全局变量mTemporaryScreenBrightness
,然后等待SettingsProvider中保存的亮度值发生改变,当改变完成后,mPendingScreenBrightnessSetting
将从SettingsProvider中读取到新的值,然后将使用mTemporaryScreenBrightness
作为系统亮度值,并将mTemporaryScreenBrightness
重置为-1.
之后的流程和8.1相比差别不大,最终会调用animateScreenBrightness()
方法去设置亮度。
通过以上的分析可以发现,将用户调节亮度值表示为"Temporary"也是有原因的,因为在设置完成后,他将又变为-1。
手动调节亮度的时序图如下图所示:
1.2.在视频播放界面手动调节
这块流程还是保留在PMS中,和android 8.1保持一致,因此就不再进行分析。
2.自适应背光调节
android P背光设置流程中最大的差异,是自动背光调节流程。在Google IO上提出,Android P的新特性之一就是自适应背光。Google和DeepMind合作,利用机器学习,创建了自适应背光,通过了解用户在环境中设定亮度滑块的方式,学习你的习惯,从而自动完成亮度调节。下面我们就来看看,androidP中新的自适应背光。
首先我们看自适应背光的架构,了解一些类的功能后,再分析其流程。
2.1.类架构
自动背光相关类结构如下:
其中AutomaticBrightnessController中的功能进一步紧收,只进行环境光强的监听后的一些处理,将背光曲线的创建等工作,交给了BrightnessMappingStrategy,它将负责曲线的创建,自动背光值的计算等,当获取自动背光值时,AutomaticBrightnessController将调用BrightnessMappingStrategy的接口获取。
而BrightnessMappingStrategy在创建曲线时,则需要从BrightnessConfigure类中读取两个数组源:config_autoBrightnessLevels
和config_autoBrightnessDisplayValuesNits
。
现在我们进入流程的分析。
2.2.创建背光样条曲线
在9.0中,自动背光曲线的创建放在了BrightnessMappingStrategy中,当系统启动后,进入DisplayPowerController构造方法后,就会开始创建背光曲线。
public DisplayPowerController(Context context,
DisplayPowerCallbacks callbacks, Handler handler,SensorManager sensorManager, DisplayBlanker blanker) {
//获取映射Lux-Nits-Backlight值的对象
mBrightnessMapper = BrightnessMappingStrategy.create(resources);
}
我们从BrightnessMappingStrategy.create(resources)
进入,来查看曲线的绘制,create()
方法如下,相关代码已进行注释:
@Nullable
public static BrightnessMappingStrategy create(Resources resources) {
//Lux值的数组,getLuxLevels()中会将Lux[0] = 0.
float[] luxLevels = getLuxLevels(resources.getIntAr