美文网首页
Android DisplayManagerService--0

Android DisplayManagerService--0

作者: DarcyZhou | 来源:发表于2023-11-28 08:22 被阅读0次

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

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

    相关文章

      网友评论

          本文标题:Android DisplayManagerService--0

          本文链接:https://www.haomeiwen.com/subject/pbzobdtx.html