Android P 背光机制流程分析

在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.getIntArray(
            com.android.internal.R.array.config_autoBrightnessLevels));
    //Lux值对应的背光值,9.0中将不会使用他
    int[] brightnessLevelsBacklight = resources.getIntArray(
            com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
    //描述和Lux值对应的屏幕亮度的Nits值数组,长度比Lux值数组大1。如果配置了该值,则:
    // ---config_screenBrightnessNits必须配置
    // ---config_screenBrightnessBacklight必须配置
    float[] brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(
            com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));
    //用户可调整的最大Gama值
    float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
            com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
            1, 1);
    //描述屏幕亮度的nits值数组
    float[] nitsRange = getFloatArray(resources.obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits));
    //描述与nitsRange 数组中的亮度值(单位为Nits)相对应的屏幕背光值
    int[] backlightRange = resources.getIntArray(
            com.android.internal.R.array.config_screenBrightnessBacklight);
    //判断是否是有效映射:1.非空;2.长度相同;3.元素>=0;4.nitsRange/luxLevels必须递增,backlightRange/brightnessLevelsNits必须非递减
    if (isValidMapping(nitsRange, backlightRange)
            && isValidMapping(luxLevels, brightnessLevelsNits)) {
        //最小背光值6
        int minimumBacklight = resources.getInteger(
                com.android.internal.R.integer.config_screenBrightnessSettingMinimum);
        //最大背光值255
        int maximumBacklight = resources.getInteger(
                com.android.internal.R.integer.config_screenBrightnessSettingMaximum);
        //获取BrightnessConfiguration.Builder实例
        BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
        //将读取的Lux值和nits值保存在builder对象中
        builder.setCurve(luxLevels, brightnessLevelsNits);
        //映射Lux值和Nits值,而非Lux值和直接显示的背光值,物理映射
        return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange,
                autoBrightnessAdjustmentMaxGamma);
    } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) {
        //直接映射Lux值和背光值,简单映射
        return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight,
                autoBrightnessAdjustmentMaxGamma);
    } else {
        return null;
    }
}

create()方法中,首先读取config.xml文件中的配置值,然后根据这个配置值决定映射方式。
在9.0之前,自动背光只需配置Lux值和对应的Backlight值来创建简单的映射,在9.0中,摒弃了这种方式,额外增加了三个配置值,并根据这些配置值决定是使用物理映射还是简单映射,涉及到的配置值如下:

<!-- Lux值数组-->
<integer-array name="config_autoBrightnessLevels">
</integer-array>
<!-- Lux值对应的背光值数组 -->
<integer-array name="config_autoBrightnessLevels">
</integer-array>
<!-- Lux值对应的Nits值数组 -->
<array name="config_autoBrightnessDisplayValuesNits">
</array>
<!-- 描述屏幕发光强度的Nits值数组 -->
<array name="config_screenBrightnessNits">
</array>
<!-- 和发光强度Nits值对应的背光值数组 -->
<integer-array name="config_screenBrightnessBacklight">
</integer-array>

在以上四个配置值中,后3组是9.0新添加,如果没有配置这三组,则系统将使用前两组配置值创建简单映射关系。因此,9.0中必须配置后三个值,以使用物理映射关系,当配置这些值后,config_autoBrightnessLevels将不再使用。

读取完配置值后,将Lux值数组和Lux值对应的Nits值数组通过setCurve()方法赋值给了builder对象,最终会作为BrightnessConfiguration对象的全局变量。
BrightnessConfiguration表示亮度的配置类,其中保存了Lux值数组和Lux值对应的Nits值数组:

private BrightnessConfiguration(float[] lux, float[] nits, String description) {
    mLux = lux;
    mNits = nits;
    mDescription = description;
}

同时,提供了一个getCurve()接口,用于提供它的mLux和mNits。

接下来进入到物理映射关系对象PhysicalMappingStrategy的创建,看其构造方法:

/**
 * @param config BrightnessConfiguration对象,携带有用于创建曲线的Lux数组和对应的Nits数组
 * @param nits 描述屏幕发光强度的nits值数组
 * @param backlight 描述与nits值数组对应的背光值
 * @param maxGamma 用户可调整最大Gama值
 */
public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits,
                               int[] backlight, float maxGamma) {
    mMaxGamma = maxGamma;
    //自动亮度调节值
    mAutoBrightnessAdjustment = 0; 
    //在自动背光开启的情况下,用户手动调节亮度时的当前Lux值
    mUserLux = -1;
    //在自动背光开启的情况下,用户手动调节设置的亮度
    mUserBrightness = -1;

    // Setup the backlight spline
    final int N = nits.length;
    float[] normalizedBacklight = new float[N];
    //将背光值/255后,存储在normalizedBacklight数组中
    for (int i = 0; i < N; i++) {
        normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]);
    }
    //创建Nits-Backlight样条曲线
    mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight);
    //创建Backlight-Nits样条曲线
    mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
    mDefaultConfig = config;
    mConfig = config;
    //将根据不同的场景,创建Lux-Nits样条曲线
    computeSpline();
}

在PhysicalMappingStrategy的构造方法中,首先根据 config_screenBrightnessNits 数组和config_screenBrightnessBacklight 数组,创建了两条映射曲线,然后调用 computeSpline() 方法。
computeSpline()是很重要的一个方法,这个方法中除了创建另外一条Lux-Nits曲线外,还会根据用户当前对亮度的调整,插入用户调整后的值,并调整Lux-Nits曲线,该方法如下:

private void computeSpline() {
    //得到BrightnessConfiguration中的Lux数组和Lux值对应的Nits数组,并放入Pair对象中
    Pair<float[], float[]> defaultCurve = mConfig.getCurve();
    //Lux数组
    float[] defaultLux = defaultCurve.first;
    //和Lux数组映射的Nits数组
    float[] defaultNits = defaultCurve.second;
    //创建一个和defaultNits数组等长的数组,用来存放对应的背光值,从Nits-backlights曲线中获取
    //根据Lux-Nit值,从NitsToBacklight曲线中获取背光值
    //即根据config_autoBrightnessDisplayValuesNits值从config_screenBrightnessNits与config_screenBrightnessBacklight的曲线中获取默认的背光值
    float[] defaultBacklight = new float[defaultNits.length];
    for (int i = 0; i < defaultBacklight.length; i++) {
        defaultBacklight[i] = mNitsToBacklightSpline.interpolate(defaultNits[i]);
    }
    //对得到的默认背光值进一步加工,如果用户设置过亮度,需要将用户设置的亮度值添加进曲线,得到
    //调整后的Lux值数组和backlight值数组
    Pair<float[], float[]> curve = getAdjustedCurve(defaultLux, defaultBacklight, mUserLux,
            mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma);
    //最终的Lux值和背光值
    float[] lux = curve.first;
    float[] backlight = curve.second;
    float[] nits = new float[backlight.length];
    //根据背光值,从config_screenBrightnessNits和onfig_screenBrightnessBacklight构建的mBacklightToNitsSpline曲线中获取Nit值
    for (int i = 0; i < nits.length; i++) {
        nits[i] = mBacklightToNitsSpline.interpolate(backlight[i]);
    }
    //Lux-Nits曲线,最终的背光值从此曲线+mNitsToBacklightSpline曲线获取
    mBrightnessSpline = Spline.createSpline(lux, nits);
}

在这个方法中,首先调用 mConfig.getCurve() 方法,获取了mConfig对象中的mLux和mNit的拷贝,这两个值就是在创建mConfig时传入的 config_autoBrightnessLevels 数组和config_autoBrightnessDisplayValuesNits 数组。
然后利用得到的nits值从曲线mNitsToBacklightSpline中得到背光值数组defaultBacklight;

接下来,调用getAdjustedCurve()方法对defaultBacklight[]做进一步加工,如果在自动背光打开的情况下,用户没有通过亮度条调节背光,则将返回原数据。此处暂且认为没有操作过亮度条,对getAdjustedCurve()先不做分析。

最后,利用defaultBacklight数组从曲线 mBacklightToNitsSpline 中得到Nits值,然后,创建表示Lux值和对应Nits值的曲线 mBrightnessSpline,这也是创建的最后一条全局样条曲线(在某些方法中会创建一些临时曲线)。

到这里为止,对自动背光样条曲线的创建就分析完成了,整体来看,共创建了三条样条曲线,对Lux-Nit-Backlight进行映射。而且和8.1不同的是,并非直接由Lux和Baclight映射,而是将Nit作为Lux和Backlight的中间介质。这样做相比之前版本中,有什么好处呢?

这里简单引用一些国际单位制的定义:

光照度:从光源照射到单位面积上的光通量,以E表示,照度的单位为勒克斯(Lux,简称lx);

光亮度:指一个表面的明亮程度,以L表示,即从一个表面反射出来的光通量.不同物体对光有不同的反射系数或吸收系数.光的强度可用照在平面上的光的总量来度量,这叫入射光(inci-dentlight)或照度(illuminance).若用从平面反射到眼球中的光量来度量光的强度,这种光称为反射光或亮度.例如,一般白纸大约吸收入射光量的20%,反射光量为80%;黑纸只反射入射光量的3%.所以,白纸和黑纸在亮度上差异很大。

光照度和光亮度的关系可以用如下公式表示:
L=R×E,式中L为亮度,R为反射系数,E为照度.

因此,从光学角度而言,我们感知的亮度是指从屏幕反射到眼球中的光的强度,而且这个强度跟光亮度(Nit)有一定关系,光亮度又跟光照度(Lux)有一定关系,因此,如果严格考虑光照度、光亮度、入射光、反射光等调节,相比通过Lux值决定背光值而言,通过Lux值决定Nit值,再由Nit值决定背光值,无疑是最精准的。

下图表示了四个曲线的创建过程:

创建样条曲线的时序图如下:

了解了曲线的创建后,下面我们开始分析自动背光的调节过程。

还是从DisplayPowerController中开始,对光照强度的采集、获取自动背光都在AutomaticBrightnessController中进行,我们从它的创建和配置分别对它进行分析。

2.3.AutomaticBrightnessController的初始化

这里对AutomaticBrightnessController的创建就不进行细致的分析,和8.1相比,多添加了3个成员,其余内容变化不大,请看8.1中的分析。
其中第一个成员是BrightnessMappingStretagy实例,由构造方法传入,用来创建曲线、获取自动背光值:

//DisplayPowerController.updatePowerState()中

//获取映射Lux-Nits-Backlight值的对象
mBrightnessMapper = BrightnessMappingStrategy.create(resources);
//初始化AutomaticBrightnessController
if (mBrightnessMapper != null) {
    //实例化自动背光控制器
    mAutomaticBrightnessController = new AutomaticBrightnessController(this,
            handler.getLooper(), sensorManager, mBrightnessMapper,
            lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum,
            mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
            initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
            autoBrightnessResetAmbientLuxAfterWarmUp, hysteresisLevels);
} else {
    mUseSoftwareAutoBrightnessConfig = false;
}

 剩余两个成员为mShortTermModelValidmShortTermModelAnchor:

private boolean mShortTermModelValid;
private float mShortTermModelAnchor;

这两个值大概意思是"短期模型是否有效?"什么是短期模型呢?就是用户设置的背光控制点。当短期模型无效后,并不会立即重置数据,而是等待环境光发生较大变化后,会清除用户设置数据,具体内容会在下面分析。

2.4.AutomaticBrightnessController的配置

在DisplayPowerController中,每一次调用 updatePowerState() 更新状态时,都会对AutomaticBrightnessController进行配置:

boolean hadUserBrightnessPoint = false;
// Configure auto-brightness.
if (mAutomaticBrightnessController != null) {
    //曲线中是否有用户设置的短期点
    hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
    //配置mAutomaticBrightnessController
    mAutomaticBrightnessController.configure(autoBrightnessEnabled,
            mBrightnessConfiguration,
            mLastUserSetScreenBrightness / (float) PowerManager.BRIGHTNESS_ON,
            userSetBrightnessChanged, autoBrightnessAdjustment,
            autoBrightnessAdjustmentChanged, mPowerRequest.policy);
}

在以上逻辑中,hadUserBrightnessPoint表示是否在自动背光打开的情况下拖动亮度条调节过亮度,判断依据是BrightnessMappingStrategy中的mUserLux成员,它表示用户在开启自动背光后手动设置亮度时的Lux值:

@Override
public boolean hasUserDataPoints() {
    return mUserLux != -1;
}

然后开始调用configure()方法进行配置,先来看看这些参数:

  • autoBrightnessEnabled:表示自动背光是否可用,由以下值决定:
//打开了自动亮度调节&&(亮屏或Doze)&&局部变量brightness为0&&BrightnessMappingStrategy不为空
final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness
            && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
            && brightness < 0
            && mAutomaticBrightnessController != null;
  • mBrightnessConfiguration:BrightnessConfiguration对象,携带有用于创建曲线的Lux值数组和对应的Nit值数组,每一个用户可对应一个BrightnessConfiguration,由DisplayManagerService负责设置。
  • brightness:当前亮度值/255的结果,区间为(0,1.0].
  • userChangedBrightness:表示用户是否手动通过拖动亮度条设置过亮度。
  • userChangedAutoBrightnessAdjustment:表示自动背光调整值adjustment是否发生变化。
  • displayPolicy:当前请求的屏幕状态。

Configure()方法如下:

/**
 * @param enable 自动背光功能是否可用
 * @param configuration BrightnessConfiguration对象
 * @param brightness 当前背光值/255的形式
 * @param userChangedBrightness  用户是否改变过背光值
 * @param adjustment 自动背光调节值
 * @param userChangedAutoBrightnessAdjustment 用户是否改变过自动背光调节值
 * @param displayPolicy PMS中要请求的Diplay状态值
 */
public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
        float brightness, boolean userChangedBrightness, float adjustment,
        boolean userChangedAutoBrightnessAdjustment, int displayPolicy) {
    //是否屏幕要进入Doze状态
    boolean dozing = (displayPolicy == DisplayPowerRequest.POLICY_DOZE);
    //设置BrightnessConfigure对象,若BrightnessConfigure发生改变,返回true
    boolean changed = setBrightnessConfiguration(configuration);
    //设置Display状态,若发生改变,返回true
    changed |= setDisplayPolicy(displayPolicy);
    //如果用户改变自动背光调节值,设置自动背光调节值
    if (userChangedAutoBrightnessAdjustment) {
        changed |= setAutoBrightnessAdjustment(adjustment);
    }
    //如果在自动亮度开启的情况下调节了亮度,需要将当前的Lux值和用户设置的亮度添加到曲线中
    if (userChangedBrightness && enable) {
        // Update the brightness curve with the new user control point. It's critical this
        // happens after we update the autobrightness adjustment since it may reset it.
        changed |= setScreenBrightnessByUser(brightness);
    }
    final boolean userInitiatedChange =
            userChangedBrightness || userChangedAutoBrightnessAdjustment;
    if (userInitiatedChange && enable && !dozing) {
        //做旧值的记录
        prepareBrightnessAdjustmentSample();
    }
    //注册解除注册LSensor
    changed |= setLightSensorEnabled(enable && !dozing);
    //如果changed为true,更新自动背光亮度值,但不会主动调用DPC更新背光
    if (changed) {
        updateAutoBrightness(false /*sendUpdate*/);
    }
}

在这个方法中,囊括了所有的配置,我们对这些配置进行拆分,一个一个地分析它们。

回到configure()方法中,首先看第一个配置:

1.setBrightnessConfiguration():

这个方法负责配置当前自动背光的BrighnessConfiguration对象,在前面也说了,这个对象中带有Lux值和Nits值,用来创建曲线和获取背光值。该方法如下:

public boolean setBrightnessConfiguration(BrightnessConfiguration configuration) {
    if (mBrightnessMapper.setBrightnessConfiguration(configuration)) {
        //如果config对象发生改变,重置用户设置的曲线点
        resetShortTermModel();
        return true;
    }
    return false;
}

这个方法中,又调用进入了 mBrightnessMapper 中,继续看看:

@Override
public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) {
    if (config == null) {
        config = mDefaultConfig;
    }
    if (config.equals(mConfig)) {
        return false;
    }
    if (DEBUG) {
        PLOG.start("brightness configuration");
    }
    //如果config对象发生改变,则替换后,调用computeSpline重新创建Lux-Nits曲线
    mConfig = config;
    computeSpline();
    return true;
}

从以上方法中得知,如果BrightnessConfiguration对象发生改变,则会根据BrightnessConfiguration 中携带的Lux和Nit值数组重新创建曲线,并清除之前用户的设置。
那么 BrightnessConfiguration 何时会改变呢?答案是切换一个新用户后。
在android9.0中,针对每个用户,都会有一个 BrightnessConfiguration 和它映射,所以当切换用户后,设备的背光将可能发生改变。

BrightnessConfiguration 发生改变且在 BrighnessMappingStragety 中设置完成后,将会通过resetShortTermModel()清除原有用户的配置:

public void resetShortTermModel() {
//清除用户添加的曲线点
    mBrightnessMapper.clearUserDataPoints();
//表示短期模型仍旧有效
    mShortTermModelValid = true;
    mShortTermModelAnchor = -1;
}

clearUserDataPoint()方法如下:

@Override
public void clearUserDataPoints() {
    if (mUserLux != -1) {
        //回复自动背光调节值为0
        mAutoBrightnessAdjustment = 0;
        mUserLux = -1;//用户设置亮度时Lux值
        mUserBrightness = -1;//用户设置亮度
        computeSpline();//重新创建曲线
    }
}

在这个方法中将全局自动背光调节值、用户设置亮度值和设置亮度时Lux值进行重置,然后重新创建曲线。

2.setDisplayPolicy()

这个方法主要根据屏幕状态来设置 mShortTermModelValid 的值,该方法如下:

private boolean setDisplayPolicy(int policy) {
    if (mDisplayPolicy == policy) {
        return false;
    }
    final int oldPolicy = mDisplayPolicy;
    mDisplayPolicy = policy;
    //如果Display状态值由交互变为不可交互状态
    if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
        //发送一个30s的延迟消息,30s后将mShortTermModelValid置为false
        mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL,
                SHORT_TERM_MODEL_TIMEOUT_MILLIS);
    //如果Display状态值由不可交互变为交互状态,移除延时消息的发送
    } else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) {
        mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL);
    }
    return true;
}

从这个方法中可以看出,当屏幕状态进入Doze或者Asleep后,会发送一个定义Handler,并在到达时间后将 mShortTermModelValid 值置为false,该值的用处,在下文说明。

3.setAutoBrightnessAdjustment()

这个方法用来设置自动背光调整值,前提是自动背光调整值已经发生变化,调用此方法后,将调用到 BrightnessMappingStragety 中的同名方法中,如下:

@Override
public boolean setAutoBrightnessAdjustment(float adjustment) {
    //对adjust进行限制,取值范围为[-1,1]
    adjustment = MathUtils.constrain(adjustment, -1, 1);
    if (adjustment == mAutoBrightnessAdjustment) {
        return false;
    }
    mAutoBrightnessAdjustment = adjustment;
    //重新创建曲线
    computeSpline();
    return true;
}

在这个方法中,如果mAutoBrightnessAdjustment发生变化,也会重新创建曲线,这里也可以看得出来,曲线的创建和自动背光调整值有着莫大的关系,在默认情况下,这个值为0.那么何时这个值将发生变化,以及如何变化呢?答案留在下文。

4.setScreenBrightnessByUser()

这个方法用于将用户拖动亮度条设置的亮度和当时的Lux值添加到用于创建曲线的数组中,并重新创建曲线:

//添加用户设置亮度
private boolean setScreenBrightnessByUser(float brightness) {
    if (!mAmbientLuxValid) {
        // If we don't have a valid ambient lux then we don't have a valid brightness anyways,
        // and we can't use this data to add a new control point to the short-term model.
        return false;
    }
    //将当前的Lux值和用户设置的背光值添加到曲线中
    mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
    //设置mShortTermModelAnchor为当前Lux值 
    mShortTermModelValid = true;
    mShortTermModelAnchor = mAmbientLux;
    return true;
}

关于addUserDataPoint()方法,会在下文中进行分析。

5.prepareBrightnessAdjustmentSample()

这个方法主要是当配置元素发生改变后,对旧值做一个记录,并打印一些Log,这里就不再分析,直接略过。

6.setLightSensorEnabled()

这个方法负责LightSensor的注册和解除注册,当设备处于亮屏状态且打开自动背光功能时,将注册一个LightSensor,以监听环境光的强度变化,该方法如下:

private boolean setLightSensorEnabled(boolean enable) {
    if (enable) {
        if (!mLightSensorEnabled) {
            //表示LSensor启用
            mLightSensorEnabled = true;
            //LSensor开启的时间
            mLightSensorEnableTime = SystemClock.uptimeMillis();
            //当前LSensor的事件速率
            mCurrentLightSensorRate = mInitialLightSensorRate;
            //注册LSensor
            mSensorManager.registerListener(mLightSensorListener, mLightSensor,
                    mCurrentLightSensorRate * 1000, mHandler);
            return true;
        }
    } else if (mLightSensorEnabled) {
        mLightSensorEnabled = false;
        mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
        mRecentLightSamples = 0;
        mAmbientLightRingBuffer.clear();
        mCurrentLightSensorRate = -1;
        mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
        mSensorManager.unregisterListener(mLightSensorListener);
    }
    return false;
}

这个方法比较简单,当环境光强度发生变化后,将回调mLightSensorListener接口的方法来更新:

private final SensorEventListener mLightSensorListener = new SensorEventListener() {
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (mLightSensorEnabled) {
            final long time = SystemClock.uptimeMillis();
            final float lux = event.values[0]; 
            //处理LSensor上报事件
            handleLightSensorEvent(time, lux);
        }
    }
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // Not used.
    }
};

LSensor事件的处理先不分析,来看下一个configure()中调用的方法.

7.updateAutoBrightness()

这个方法用来更新最终计算的自动亮度:

private void updateAutoBrightness(boolean sendUpdate) {
    if (!mAmbientLuxValid) {
        return;
    }

    //根据Lux值从样条曲线中获取对应的背光值,区间为(0,1.0]的浮点数
    float value = mBrightnessMapper.getBrightness(mAmbientLux);
    //得到最终的自动背光值
    int newScreenAutoBrightness =
            clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
    if (mScreenAutoBrightness != newScreenAutoBrightness) {
        mScreenAutoBrightness = newScreenAutoBrightness;
        //是否主动调用DPC中进行背光的更新
        if (sendUpdate) {
            mCallbacks.updateBrightness();
        }
    }
}

在这个方法中,通过当前的Lux值从mBrightnessMapper中获取到了最终的背光值,我们来看看是如何获取得到背光值的:

@Override
public float getBrightness(float lux) {
//先根据Lux值,从mBrightnessSpline曲线中寻找对应的Nit值
    float nits = mBrightnessSpline.interpolate(lux);
//再根据Nit值,从mNitsToBacklightSpline曲线中寻找读应的背光值
    float backlight = mNitsToBacklightSpline.interpolate(nits);
    return backlight;
}

从这个方法可以得知读取自动背光值的原理:首先会根据当前的Lux值,从mBrightnessSpline曲线中寻找对应的Nit值,然后根据Nit值,从曲线mNitsToBacklightSpline中获取到最终的背光值。在分析样条曲线的创建时已知道,mBrightnessSpline曲线是由Lux值数组和它对应的Nit值数组创建,mNitsToBacklightSpline是由配置文件中的config_screenBrightnessNitsconfig_screenBrightnessBacklight创建。具体的创建在computeSline()方法中。

经过以上的分析,自动背光的获取及和配置数组值的关系,可以用下图来表示:

2.5.adjustment值的计算

在android 8.1中,当用户在开启自动背光的场景下手动拖动亮度条调节时,实际上是调用setScreenAutoBrightnessAdjustment()方法调整了自动背光调整值,然后通过自动背光调整值计算得出新的自动背光值。

在9.0中,则完全不同,它会直接设置用户选择的亮度值作为设备新的背光值,还会将此时的环境光强Lux值和设置的亮度值作为创建样条曲线的控制点添加到创建样条曲线的数组中,并重新创建样条曲线,我们可以通过Log来简单的查看,在用户未设置亮度前,系统根据配置值创建的曲线控制点如下(格式为(Lux,backlight)):

BrightnessMappingStrategy: [PLOG 1544753613130] curve: unadjusted curve: 
[(0.0,0.11764706),
(16.0,0.1579153),
(32.0,0.19611156),
(50.0,0.23574594),
(100.0,0.27560648),
(140.0,0.3135872),
(180.0,0.39658308),
(240.0,0.39658308),
(300.0,0.39658308),
(600.0,0.39658308),
(800.0,0.39658308),
(1000.0,0.7058824),
(2000.0,0.783783),
(3000.0,0.8235294),
(4000.0,0.84214103),
(5000.0,0.8603607),
(6000.0,0.90202725),
(8000.0,1.0),
(10000.0,1.0),]

 当用户将亮度设置为最大后,将变成:

BrightnessMappingStrategy: [PLOG 1544753876189] curve: smoothed curve:
[(0.0,0.4899973),
(16.0,0.54051536),
(32.0,0.58098876),
(50.0,0.6177528),
(100.0,0.6507734),
(140.0,0.67939043),
(180.0,0.7347023),
(240.0,0.7347023),
(300.0,0.7347023),
(600.0,0.7347023),
(800.0,0.7347023),
(1000.0,0.8903842),
(1505.0,1.0),
(2000.0,1.0),
(3000.0,1.0),
(4000.0,1.0),
(5000.0,1.0),
(6000.0,1.0),
(8000.0,1.0),
(10000.0,1.0),]

其中(1505.0,1.0)这组值正是用户设置亮度时的Lux值和设置的背光值,其余的点也进行了对应的调整。

除此之外,在android 9.0中,自动背光的调整值将不再由用户主动设置(虽然在DMS中保留了setTemporaryAutoBrightnessAdjustment()用于调整adjustment值),而是根据用户的调整自动进行推断得出。下面就从代码中开始分析这些新机制的实现。

当通过亮度条调节亮度时,将调用进入DisplayPowerController(以下简称DPC)的setTemporaryAutoBrightnessAdjustment()方法,并执行手动亮度调节流程,然后执行自动背光控制器的配置流程.由于用户对亮度的手动设置,因此将有:

  • mLastUserSetScreenBrightness发生改变,由默认0变为用户设置的亮度值;
  • userSetBrightnessChanged由false变为true。

此时这两个值已经发生改变,但其他值还未发生变化。
那么结合AutomaticBrightnessController(以下简称ABC)的配置,在调用configure()方法时将调用setScreenBrightnessByUser()方法,这个方法在2.4中说过了,用于将用户拖动亮度条设置的亮度和当时的Lux值添加到用于创建曲线的数组中,具体是调用BrigtnessMappingStrategy对象的addUserDataPoint()方法实现,而这个方法中,将会通过计算得出新的adjustment值来,该方法如下:

addUserDataPoint()

@Override
public void addUserDataPoint(float lux, float brightness) {
    //获取默认配置值创建的曲线中Lux值对应的背光值
    float unadjustedBrightness = getUnadjustedBrightness(lux);
    //通过传入的三个值推段自动背光调整值
    float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
            brightness /* desiredBrightness */,
            unadjustedBrightness /* currentBrightness */);
    //更新自动背光调整值
    mAutoBrightnessAdjustment = adjustment;
    //表示用户在开启自动背光情况下拖动亮度条调节亮度时的当时Lux值
    mUserLux = lux;
    //表示用户在开启自动背光情况下拖动亮度条调节亮度值
    mUserBrightness = brightness;
    //重新创建曲线
    computeSpline();
}

在这个方法中,首先将从配置文件中的数组值创建的原始样条曲线中获取当前Lux的值,使用getUnadjustedBrightness()方法:

private float getUnadjustedBrightness(float lux) {
    //得到BrightnessConfiguration中的Lux-Nit映射
    Pair<float[], float[]> curve = mConfig.getCurve();
    //创建一个Lux-Nit样条曲线
    Spline spline = Spline.createSpline(curve.first, curve.second);
    //根据Lux值得到Nit值,再根据Nit值从mNitsToBacklightSpline得到相对应的背光值
    return mNitsToBacklightSpline.interpolate(spline.interpolate(lux));
}

然后将开始计算自动背光调整值,调用inferAutoBrightnessAdjustment()方法,并且将根据配置文件中的MaxGamma值、从原始样条曲线中获取的此Lux对应的背光值以及用户所希望要设置的背光值,计算出adjustment:

inferAutoBrightnessAdjustment()

    //推断adjustment值
    private static float inferAutoBrightnessAdjustment(float maxGamma,
            float desiredBrightness, float currentBrightness) {
        float adjustment = 0;
        float gamma = Float.NaN;
        // 当前Lux值对应的默认配置亮度值 <=25.5 || >= 229.5
        if (currentBrightness <= 0.1f || currentBrightness >= 0.9f) {
            adjustment = (desiredBrightness - currentBrightness);
        //亮度为0,最低只能是6,貌似不可能
        } else if (desiredBrightness == 0) {
            adjustment = -1;
        //最大亮度,255
        } else if (desiredBrightness == 1) {
            adjustment = +1;
        } else {
            // current^gamma = desired => gamma = log[current](desired)
            //根据换底公式,得到currentBrightness为底desiredBrightness的对数
            gamma = MathUtils.log(desiredBrightness) / MathUtils.log(currentBrightness);
            // max^-adjustment = gamma => adjustment = -log[max](gamma)
            //maxGamma为底gamma的对数
            adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma);
        }
        adjustment = MathUtils.constrain(adjustment, -1, +1);
        return adjustment;
    }

在这个方法中可以看出,adjustment的计算由两个计算步骤得到,大概解释一下就是:current的gamma次方等于desired,从而得出gamma,而gamma又等于maxGamma的-adjustment次方,从而得出adjustment。

到这里,自动背光的调整值adjustment已经计算出了,接下来再看看有了adjustment后如何调整背光样条曲线。

回到addUserDataPoint()方法中,计算完毕adjustment后,会将用户设置的Lux和Brightness分别赋给BrightnessMappingStrategy对象成员mUserLux和mUserBrightness,并调用computeSpline()重新创建样条曲线,这个方法在2.2中分析了,但是对其中调用的getAdjustedCurve()没有分析,此处正适合对它做一个了解,该方法如下:

getAdjustedCurve()


    /**
     * 得到调整弧度
     * @param lux Lux值数组
     * @param brightness 根据Lux值->Lux值对应的Nits值->Nits-Backlight曲线得到的最终背光值数组
     * @param userLux 当前Lux值?
     * @param userBrightness 当前用户设置亮度值?
     * @param adjustment 自动背光调整值
     * @param maxGamma 最大Gama值
     * @return
     */
    private static Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
            float userLux, float userBrightness, float adjustment, float maxGamma) {
        //初始化两个新数组,分别存储Lux值和背光值
        float[] newLux = lux;
        float[] newBrightness = Arrays.copyOf(brightness, brightness.length);
        //限定adjustment为[-1,1]
        adjustment = MathUtils.constrain(adjustment, -1, 1);
        //maxGamma 的 -adjustment次方得到gamma值
        float gamma = MathUtils.pow(maxGamma, -adjustment);
        //gamma != 1说明adjustment不为0
        if (gamma != 1) {
            //重新设置亮度值,新的亮度值为就亮度值的gamma次方
            for (int i = 0; i < newBrightness.length; i++) {
                newBrightness[i] = MathUtils.pow(newBrightness[i], gamma);
            }
        }
        //用户在BrightnessController拖动条上手动调节了亮度
        if (userLux != -1) {
            //插入一对新的Lux和对应背光值
            Pair<float[], float[]> curve = insertControlPoint(newLux, newBrightness, userLux,
                    userBrightness);
            newLux = curve.first;
            newBrightness = curve.second;
        }
        return Pair.create(newLux, newBrightness);
    }

在以上方法中,通过adustment值获取gamma值,再通过gamma值调整整个背光数组元素,仔细一看,其实在这里,计算新的亮度值的算法,就是inferAutoBrightnessAdjustment()中的反操作,这里是通过adjustment得到gamma,再由gamma得到新的背光值数组。

计算出新的背光值后,调用insertControlPoint()将用户设置的点作为曲线控制点插入相关数组中:


    private static Pair<float[], float[]> insertControlPoint(
            float[] luxLevels, float[] brightnessLevels, float lux, float brightness) {
        //找到插入点索引,以保持值的递增
        final int idx = findInsertionPoint(luxLevels, lux);
        final float[] newLuxLevels;
        final float[] newBrightnessLevels;
        //如果插入索引等于数组长度,则:
        if (idx == luxLevels.length) {
            //需要插入到末尾位置,且新Lux数组长度比原来大1
            newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + 1);
            newBrightnessLevels  = Arrays.copyOf(brightnessLevels, brightnessLevels.length + 1);
            newLuxLevels[idx] = lux;
            newBrightnessLevels[idx] = brightness;
        //如果用户lux值已处于Lux值数组中,则:
        } else if (luxLevels[idx] == lux) {
            //copy原数组即可
            newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length);
            newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length);
            newBrightnessLevels[idx] = brightness;
        //否则:
        } else {
            //初始化新Lux数组,长度比原数组大1
            newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + 1);
            //将原数组从idx位置起的元素,向后移动1位,将新的Lux值插入到idx位置
            System.arraycopy(newLuxLevels, idx, newLuxLevels, idx+1, luxLevels.length - idx);
            newLuxLevels[idx] = lux;
            //初始化新亮度值数组,长度比原数组大1
            newBrightnessLevels  = Arrays.copyOf(brightnessLevels, brightnessLevels.length + 1);
            //将原数组从idx位置起的元素,向后移动1位,将新的亮度值值插入到idx位置
            System.arraycopy(newBrightnessLevels, idx, newBrightnessLevels, idx+1,
                    brightnessLevels.length - idx);
            newBrightnessLevels[idx] = brightness;
        }
        //平滑曲线,newLuxLevels之后的Lux对应的亮度值将全部为newBrightnessLevels
        smoothCurve(newLuxLevels, newBrightnessLevels, idx);
        return Pair.create(newLuxLevels, newBrightnessLevels);
    }

该方法调用完毕后,返回一个携带有Lux和背光值的Pair对象给getAdjustedCurve(),在getAdjustedCurve()中将返回该Pair对象给到computeSpline()中,在computeSpline()将从返回的Pair对像中取出Lux数组值和背光数组值,完成曲线的创建。

根据上面的分析可以发现,getAdjustedCurve()中的局部变量和maxGamma、adjustment值有如下关系:

这些逻辑执行完毕后,回到configure()方法,接下来将由LSensor检测环境光强、调节亮度…这些流程就不再说明。

我们再回到DisplayPowerController中updatePowerState()中,当configure()执行完毕后,DPC中将拿到调整后的adjustment值,并完成保存,这部分代码如下:

    private void updatePowerState() {
	//......

        if (brightness < 0) {
            float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
            if (autoBrightnessEnabled) {
		//获取自动背光亮度值
                brightness = mAutomaticBrightnessController.getAutomaticScreenBrightness();
		//获取新的adjustment值
                newAutoBrightnessAdjustment =
                        mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment();
            }

            if (brightness >= 0) {

                brightness = clampScreenBrightness(brightness);
                if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
                    slowChange = true; // slowly adapt to auto-brightness
                }
                putScreenBrightnessSetting(brightness);
                mAppliedAutoBrightness = true;
            } else {
                mAppliedAutoBrightness = false;
            }
            if (autoBrightnessAdjustment != newAutoBrightnessAdjustment) {
                //将adjustment值保存到SettingsProvider中
                putAutoBrightnessAdjustmentSetting(newAutoBrightnessAdjustment);
            }
        } else {
            mAppliedAutoBrightness = false;
        }
	//......
    }

 在此处,会将ABC中计算得出的adjustment保存在SettingsProvider中,以确保Settings等其他进程对它的使用:


    private void putAutoBrightnessAdjustmentSetting(float adjustment) {
        mAutoBrightnessAdjustment = adjustment;
        Settings.System.putFloatForUser(mContext.getContentResolver(),
                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adjustment, UserHandle.USER_CURRENT);
    }

从用户拖动亮度条调节自动背光开始,到adjustment值计算完成,并最终在SettingsProvider中完成保存,整个过程时序图如下:

2.6.清除用户设置数据

使用ABC的resetShortTermModel()可以清除用户设置的亮度和对应Lux时,那么何时清除用户设置的背光值和对应Lux值呢?有以下三种情况:

1.切换用户

相关代码如下:

    private void handleSettingsChange(boolean userSwitch) {
        if (userSwitch) {
            if (mAutomaticBrightnessController != null) {
                mAutomaticBrightnessController.resetShortTermModel();
            }
        }
    }
2.BrightnessConfigure对象发生变化

这种情况在2.4中了解 setBrightnessConfiguration() 时已分析过。

3.屏幕进入不可交互状态超过30s,且再次进入交互状态后环境光强有一定范围变化

在分析setDisplayPolicy()时说到,若屏幕由交互状态进入非交互状态时,将会发送一个延时Handler消息,并在到达时间后,将mShortTermModelValid值设置为false,那么如果当屏幕状态再进入交互状态时,肯定会有设置Lux值的流程,而就在这个流程中,mShortTermModelValid的含义将彰显出来:

    private void setAmbientLux(float lux) {

        mAmbientLux = lux;
        mBrighteningLuxThreshold = mHysteresisLevels.getBrighteningThreshold(lux);
        mDarkeningLuxThreshold = mHysteresisLevels.getDarkeningThreshold(lux);

        // If the short term model was invalidated and the change is drastic enough, reset it.
        if (!mShortTermModelValid && mShortTermModelAnchor != -1) {
            //SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6
            final float minAmbientLux =
                mShortTermModelAnchor - mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
            final float maxAmbientLux =
                mShortTermModelAnchor + mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
            //如果环境光强介于这个值之间,则不会reset,否则reset
            if (minAmbientLux < mAmbientLux && mAmbientLux < maxAmbientLux) {
                mShortTermModelValid = true;
            } else {
                resetShortTermModel();
            }
        }
    }

在这个方法中,只有进入非交互状态时mShortTermModelValid才会为false,表示将用户设置置为无效状态,但不会立即置为无效状态,只有环境光强发生较大变化时才会重置用户设置,这个"较大变化"即是大于当前光强的0.6倍或者小于当前光强的0.6倍时。

关于android 9.0自动背光,就先了解这么多。此外,从代码结构来看,还增加了BrightnessTracker等类,这个类会对用户自动背光亮度进行一些记录,就暂时不进行分析了。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/351884.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[docker] Docker的私有仓库部署——Harbor

一、Docker原生私有仓库—— Registry 1.1 Registry的简单了解 关于Docker的仓库分为私有库和公有仓库&#xff0c;共有仓库只要在官方注册用户&#xff0c;登录即可使用。但对于仓库的使用&#xff0c;企业还是会有自己的专属镜像&#xff0c;所以私有库的搭建也是很有必要的…

那些年与指针的爱恨情仇(一)---- 指针本质及其相关性质用法

关注小庄 顿顿解馋 (≧∇≦) 引言&#xff1a; 小伙伴们在学习c语言过程中是否因为指针而困扰&#xff0c;指针简直就像是小说女主&#xff0c;它逃咱追&#xff0c;我们插翅难飞…本篇文章让博主为你打理打理指针这个傲娇鬼吧~ 本节我们将认识到指针本质&#xff0c;何为指针和…

将Html页面转换为Wordpress页面

问题&#xff1a;我们经常会从html源码下载网站上获得我们想要的网站内容框架&#xff0c;以及部分诸如联系我们&#xff0c;About 等内页&#xff0c;但是在文章的发布上&#xff0c;则远不如Wordpress简便。而Wordpress尽管有各种模板&#xff0c;但修改又比较麻烦。解决方法…

day34WEB 攻防-通用漏洞文件上传黑白盒审计逻辑中间件外部引用

目录 一&#xff0c;白盒审计-Finecms-代码常规-处理逻辑 黑盒思路&#xff1a;寻找上传点抓包修改突破获取状态码及地址 审计流程&#xff1a;功能点-代码文件-代码块-抓包调试-验证测试 二&#xff0c;白盒审计-CuppaCms-中间件-.htaccess 三&#xff0c;白盒审计-Metin…

CVE-2024-23897 Jenkins 任意文件读取漏洞

项目介绍 Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件项目可以进行持续集成。Jenkins是开源CI&CD软件领导者&#xff0c; 提供超过1000个插…

guhub访问

问题 访问不了guhub 解决过程 可以正常访问了 总结 此方法参考微软官方的方法&#xff0c;修改dns服务器网址&#xff0c;由原本的自动分配改为指定微软dns服务器网址

深度强化学习(王树森)笔记02

深度强化学习&#xff08;DRL&#xff09; 本文是学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。本文在ChatGPT辅助下完成。 参考链接 Deep Reinforcement Learning官方链接&#xff1a;https://github.com/wangshusen/DRL 源代码链接&#xff1a;https://github.c…

苹果手机突然无服务了,这是怎么回事?

苹果手机无疑是一款备受青睐的智能设备&#xff0c;但有时候我们可能会面临一个令人困扰的情况——苹果手机突然无服务。这种情况可能会在任何时候发生&#xff0c;无论是在使用手机时&#xff0c;刚刚升级系统&#xff0c;或者是突然发现在本应有信号的区域却无法连接到运营商…

Linux——常用命令

1、命令的基本格式 对服务器来讲&#xff0c;图形界面会占用更多的系统资源&#xff0c;而且会安装更多的服务、开放更多的端口&#xff0c;这对服务器的稳定性和安全性都有负面影响。其实&#xff0c;服务器是一个连显示器都没有的家伙&#xff0c;要图形界面干什么&#xff…

Nodejs基础4之fs模块的批量重命名练习、path模块、HTTP协议

Nodejs基础 fs模块fs练习-批量重命名fs练习-批量排序重命名 path模块resolve 解决sep 分隔符parse 方法其他的几个方法 HTTP协议http报文安装fiddler软件配置fiddler使用fiddler 请求报文结构整体请求行请求方法URLHTTP版本号 请求头请求体 响应报文结构整体响应行响应状态码响…

用甘特图有效管理多个项目进度

当公司或组织同时承担多个项目时,合理规划各项目的时间节点与资源分配对确保高效完成至关重要。采用甘特图可以直观地展示多个项目的时间进程、关键里程碑以及资源分配情况,便于从宏观层面全面把控各项目的动态。 在线甘特图软件 zz-plan.com 提供了非常强大的时间轴规划功能,支…

14、Kafka ------ kafka 核心API 之 流API(就是把一个主题的消息 导流 到另一个主题里面去)

目录 kafka 核心API 之 流APIKafka流API的作用&#xff1a;流API的核心API&#xff1a;使用流API编程的大致步骤如下&#xff1a;代码演示 流API 用法MessageStream 流API 代码演示消息从 test1主题 导流到 test2主题演示使用匿名内部类对消息进行处理Topology 拓扑结构 讲解 代…

程序员该懂的一些测试(二)单元测试与集成测试

集成测试才是目的 单元测试只是对每个小模块进行的测试&#xff0c;可以理解成每次提交的一个小功能。 最终系统的良好运行依赖于这些小模块之间良好协作。所以需要有集成测试 集成测试回滚数据&#xff0c;保证测试的可重复性 我们可以把 ApplicationContext 理解成 DI 容器…

Transformer and Pretrain Language Models3-6

Pretrain Language Models预训练语言模型 content&#xff1a; language modeling&#xff08;语言模型知识&#xff09; pre-trained langue models(PLMs&#xff09;&#xff08;预训练的模型整体的一个分类&#xff09; fine-tuning approaches GPT and BERT&#xff08;…

【探索科技 感知未来】文心一言大模型

【探索科技 感知未来】文心大模型 &#x1f6a9;本文介绍 文心一言大模型是由中国科技巨头百度公司研发的一款大规模语言模型&#xff0c;其基于先进的深度学习技术和海量数据训练而成。这款大模型具备强大的自然语言处理能力&#xff0c;可以理解并生成自然语言&#xff0c;为…

JVM-字节码应用

一、字节码的应用远超你的想象 二、ASM介绍与读取字节码实战 用CoreAPI解析和TreeAPI都能做字节码解析&#xff0c;区别&#xff0c;TreeAPI必须读取完整字节码信息&#xff0c;才能做解析。 下面代码&#xff0c;使用CoreAPI做解析&#xff1a; package asm;public class MyM…

leetcode.2846. 边权重均等查询【lca+树形dp】

原题链接&#xff1a;2846. 边权重均等查询 题目描述&#xff1a; 现有一棵由 n 个节点组成的无向树&#xff0c;节点按从 0 到 n - 1 编号。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges &#xff0c;其中 edges[i] [ui, vi, wi] 表示树中存在一条位于节点 ui …

微服务-微服务Alibaba-Nacos注册中心实现

1. 系统架构的演变 俗话说&#xff0c; 没有最好的架构&#xff0c;只有最合适的架构。 微服务架构也是随着信息产业的发展而出现的最有普 遍适用性的一套架构模式。通常来说&#xff0c;我们认为架构发展历史经历了这样一个过程&#xff1a;单体架构——> 垂直架构 ——&g…

leetcode 42.接雨水

问题1&#xff1a;怎么算接水量 总的接水量第一列接水量第二列接水量第三列接水量…最后一列接水量 问题2&#xff1a;当前列的接水量怎么计算 当前的接水量min(当前列左边最高的墙x1&#xff0c;当前列右边最高的墙x3&#xff09;- 当前列x2的高度 问题2图解&#xff1a; …

蓝桥杯-sort排序(上)

sort排序 &#x1f388;1.算法说明&#x1f388;2.例题&#x1f52d;2.1例题一&#x1f52d;2.2例题二&#x1f52d;2.3例题三&#x1f52d;2.4例题四&#x1f52d;2.5例题五&#x1f52d;2.6例题六 &#x1f388;1.算法说明 &#x1f50e;对于一个数组&#xff0c;通过对数组中…