Android DisplayManagerService--0

2023-11-28  本文已影响0人  DarcyZhou

本文转载自:Android R DisplayManagerService(4) 亮度调节

本文基于Android 11.0源码分析

1.概述

  Android中调节亮度有如下几种常见方式:

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);
                    }
                });
        }
        ......
    }

以上方法中:

从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字段值发生变化,新字段中的值也会同样进行变化,从而更新亮度。

上一篇下一篇

猜你喜欢

热点阅读