专栏首页吴小龙同學Android 亮度自动调节

Android 亮度自动调节

下拉状态栏有个亮度的进度条,如果开启了亮度自动调节开关,会随着周围光线变化,这个进度条也会随着变化,接下来就是看看这个功能是如何实现的。

源码版本

基于 Android 9.0 分析。

BrightnessDialog,位于: frameworks/base/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java ToggleSliderView,位于: frameworks/base/packages/SystemUI/src/com/android/systemui/settings/ToggleSliderView.java

DisplayPowerController,位于: frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java

AutomaticBrightnessController,位于: frameworks/base/services/core/java/com/android/server/display/AutomaticBrightnessController.java

BrightnessMappingStrategy,

概述

状态栏里亮度页面是 BrightnessDialog,其中进度条设置是 ToggleSliderView,亮度自动调节主要是 DisplayPowerController 和 AutomaticBrightnessController 两个类,当亮度发生变化时,如果关联到 ToggleSliderView,用的是 ContentObserver,Uri 为 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ。

源码梳理

1、BrightnessDialog#onCreate:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //省略部分代码
    mBrightnessController = new BrightnessController(this, icon, slider);
}

2、这里进行了 BrightnessController 初始化,来看下:

public BrightnessController(Context context, ImageView icon, ToggleSlider control) {
    //省略部分代码
    mBrightnessObserver = new BrightnessObserver(mHandler);
    //省略部分代码
}

又进行了 BrightnessObserver 初始化:

/** ContentObserver to watch brightness **/
private class BrightnessObserver extends ContentObserver {
    //省略部分代码
    private final Uri BRIGHTNESS_FOR_VR_URI =
            Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR);
    //Add By WuXiaolong for AutomaticBrightness
    private final Uri BRIGHTNESS_ADJ_URI =
            Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ);
    public BrightnessObserver(Handler handler) {
        super(handler);
    }
    @Override
    public void onChange(boolean selfChange) {
        onChange(selfChange, null);
    }
    @Override
    public void onChange(boolean selfChange, Uri uri) {
        if (selfChange) return;
        if (BRIGHTNESS_MODE_URI.equals(uri)) {
            mBackgroundHandler.post(mUpdateModeRunnable);
            mBackgroundHandler.post(mUpdateSliderRunnable);
        } 
        //省略部分代码
        //Add By WuXiaolong for AutomaticBrightness
        else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) {
            mBackgroundHandler.post(mUpdateSliderRunnable);
        } else {
            mBackgroundHandler.post(mUpdateModeRunnable);
            mBackgroundHandler.post(mUpdateSliderRunnable);
        }
        for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
            cb.onBrightnessLevelChanged();
        }
    }
    public void startObserving() {
        final ContentResolver cr = mContext.getContentResolver();
        cr.unregisterContentObserver(this);
        //省略部分代码
        cr.registerContentObserver(
                BRIGHTNESS_FOR_VR_URI,
                false, this, UserHandle.USER_ALL);
        //Add By WuXiaolong for AutomaticBrightness
        cr.registerContentObserver(
                BRIGHTNESS_ADJ_URI,
                false, this, UserHandle.USER_ALL);
    }
    public void stopObserving() {
        final ContentResolver cr = mContext.getContentResolver();
        cr.unregisterContentObserver(this);
    }
}

其实我目前下载的源码,这块功能是不全的,我已经加上了,哪里进行 BrightnessObserver 的 ContentObserver 注册呢?

3、回到 BrightnessDialog#onStart:

@Override
protected void onStart() {
    super.onStart();
    mBrightnessController.registerCallbacks();
    MetricsLogger.visible(this, MetricsEvent.BRIGHTNESS_DIALOG);
}

4、调用mBrightnessController.registerCallbacks();最终走到 mStartListeningRunnable:

private final Runnable mStartListeningRunnable = new Runnable() {
    @Override
    public void run() {
        //BrightnessObserver 注册
        mBrightnessObserver.startObserving();
        mUserTracker.startTracking();
        // Update the slider and mode before attaching the listener so we don't
        // receive the onChanged notifications for the initial values.
        mUpdateModeRunnable.run();
        mUpdateSliderRunnable.run();
        mHandler.sendEmptyMessage(MSG_ATTACH_LISTENER);
    }
};

当亮度有变化时,会走 BrightnessObserver#onChange,最终走到:

private final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        mExternalChange = true;
        try {
            switch (msg.what) {
                //省略部分代码
                case MSG_UPDATE_SLIDER:
                    updateSlider(msg.arg1, msg.arg2 != 0);
                    break;
                //省略部分代码
                default:
                    super.handleMessage(msg);
            }
        } finally {
            mExternalChange = false;
        }
    }
};

走 updateSlider方法,到 :

private void animateSliderTo(int target) {
    if (!mControlValueInitialized) {
        // Don't animate the first value since it's default state isn't mea
        mControl.setValue(target);
        mControlValueInitialized = true;
    }
    //省略部分代码
}

5、跳到 ToggleSliderView#setValue:

@Override
public void setValue(int value) {
    //这里正是修改进度条
    mSlider.setProgress(value);
    if (mMirror != null) {
        mMirror.setValue(value);
    }
}

接下来就是看看亮度自动调节主要的两个类 DisplayPowerController 和 AutomaticBrightnessController。DisplayPowerController 属于 Display 模块,其控制设备屏幕亮灭、背光、与Power关系密切,这里主要看下屏幕亮度的控制这方面的逻辑。

6、首先,在 DisplayManagerService 中初始化 DisplayPowerController,如下:

private final class LocalService extends DisplayManagerInternal {
    @Override
    public void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler,
            SensorManager sensorManager) {
        synchronized (mSyncRoot) {
            //省略部分代码
            mDisplayPowerController = new DisplayPowerController(
                    mContext, callbacks, handler, sensorManager, blanker);
        }
        mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATION);
    }

7、接着看下 DisplayPowerController 构造方法,如下:

public DisplayPowerController(Context context,
        DisplayPowerCallbacks callbacks, Handler handler,
        SensorManager sensorManager, DisplayBlanker blanker) {
    //省略部分代码
    mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
            com.android.internal.R.bool.config_automatic_brightness_available);
    //省略部分代码
    if (mUseSoftwareAutoBrightnessConfig) {
        //省略部分代码
        mBrightnessMapper = BrightnessMappingStrategy.create(resources);
        if (mBrightnessMapper != null) {
            mAutomaticBrightnessController = new AutomaticBrightnessController(this,
                    handler.getLooper(), sensorManager, mBrightnessMapper,
                    lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum,
                    mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
                    initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
                    autoBrightnessResetAmbientLuxAfterWarmUp, hysteresisLevels);
        } else {
            mUseSoftwareAutoBrightnessConfig = false;
        }
    }
    //省略部分代码
    mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
    mTemporaryAutoBrightnessAdjustment = Float.NaN;
    //省略部分代码
}

由于亮屏之后屏幕自动亮度才会生效,所以在亮屏的时候,流程会走到 DisplayPowerController 中的核心函数 updatePowerState():

private void updatePowerState() {
    // Update the power state request.
    //省略部分代码
    
    final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
    if (autoBrightnessAdjustmentChanged) {
        mTemporaryAutoBrightnessAdjustment = Float.NaN;
    }
    // Use the autobrightness adjustment override if set.
    final float autoBrightnessAdjustment;
    if (!Float.isNaN(mTemporaryAutoBrightnessAdjustment)) {
        autoBrightnessAdjustment = mTemporaryAutoBrightnessAdjustment;
        mAppliedTemporaryAutoBrightnessAdjustment = true;
    } else {
        autoBrightnessAdjustment = mAutoBrightnessAdjustment;
        mAppliedTemporaryAutoBrightnessAdjustment = false;
    }
    
    boolean hadUserBrightnessPoint = false;
    // Configure auto-brightness.
    if (mAutomaticBrightnessController != null) {
        hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
        mAutomaticBrightnessController.configure(autoBrightnessEnabled,
                mBrightnessConfiguration,
                mLastUserSetScreenBrightness / (float) PowerManager.BRIGHTNESS_ON,
                userSetBrightnessChanged, autoBrightnessAdjustment,
                autoBrightnessAdjustmentChanged, mPowerRequest.policy);
    }
    
    // Apply auto-brightness.
    boolean slowChange = false;
    if (brightness < 0) {
        float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
        if (autoBrightnessEnabled) {
            brightness = mAutomaticBrightnessController.getAutomaticScreenBrightness();
            newAutoBrightnessAdjustment =
                    mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment();
        }
        if (brightness >= 0) {
            // Use current auto-brightness value and slowly adjust to changes.
            brightness = clampScreenBrightness(brightness);
            if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
                slowChange = true; // slowly adapt to auto-brightness
            }
            // Tell the rest of the system about the new brightness. Note that we do this
            // before applying the low power or dim transformations so that the slider
            // accurately represents the full possible range, even if they range changes what
            // it means in absolute terms.
            putScreenBrightnessSetting(brightness);
            mAppliedAutoBrightness = true;
        } else {
            mAppliedAutoBrightness = false;
        }
        if (autoBrightnessAdjustment != newAutoBrightnessAdjustment) {
            // If the autobrightness controller has decided to change the adjustment value
            // used, make sure that's reflected in settings.
            putAutoBrightnessAdjustmentSetting(newAutoBrightnessAdjustment);
        }
    } else {
        mAppliedAutoBrightness = false;
    }
    //省略部分代码
}

接下来分别看看 autoBrightnessAdjustment 和 newAutoBrightnessAdjustment 怎么来的?

autoBrightnessAdjustment 是来自 mTemporaryAutoBrightnessAdjustment 或 mAutoBrightnessAdjustment 赋值,mAutoBrightnessAdjustment 在第 7 步mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();有初始化,看下 getAutoBrightnessAdjustmentSetting():

private float getAutoBrightnessAdjustmentSetting() {
    final float adj = Settings.System.getFloatForUser(mContext.getContentResolver(),
            Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f, UserHandle.USER_CURRENT);
    return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
}

继续看下 clampAutoBrightnessAdjustment:

private static float clampAutoBrightnessAdjustment(float value) {
    return MathUtils.constrain(value, -1.0f, 1.0f);
}

这里注意下 MathUtils.constrain() 表示百分比缩放函数,比如 MathUtils.constrain(0.5, 0, 255) 表示 (255-0)*0.5。

这样了解了 autoBrightnessAdjustment,接下来看 newAutoBrightnessAdjustment。

8、回到 DisplayPowerController#updatePowerState(),看到 newAutoBrightnessAdjustment 调用了 AutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment(),最终是到了 BrightnessMapper#getAutoBrightnessAdjustment() 其中 mAutoBrightnessAdjustment 变量,赋值是在 BrightnessMapper#setAutoBrightnessAdjustment

@Override
public boolean setAutoBrightnessAdjustment(float adjustment) {
    adjustment = MathUtils.constrain(adjustment, -1, 1);
    if (adjustment == mAutoBrightnessAdjustment) {
        return false;
    }
    if (DEBUG) {
        Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
                adjustment);
        PLOG.start("auto-brightness adjustment");
    }
    mAutoBrightnessAdjustment = adjustment;
    computeSpline();
    return true;
}

9、BrightnessMapper#setAutoBrightnessAdjustment 这个方法调用又回到了 AutomaticBrightnessController#setAutoBrightnessAdjustment:

private boolean setAutoBrightnessAdjustment(float adjustment) {
    return mBrightnessMapper.setAutoBrightnessAdjustment(adjustment);
}

AutomaticBrightnessController#setAutoBrightnessAdjustment调用是来到 AutomaticBrightnessController#configure()方法:

public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
        float brightness, boolean userChangedBrightness, float adjustment,
        boolean userChangedAutoBrightnessAdjustment, int displayPolicy) {
    // While dozing, the application processor may be suspended which will prevent us from
    // receiving new information from the light sensor. On some devices, we may be able to
    // switch to a wake-up light sensor instead but for now we will simply disable the sensor
    // and hold onto the last computed screen auto brightness.  We save the dozing flag for
    // debugging purposes.
    boolean dozing = (displayPolicy == DisplayPowerRequest.POLICY_DOZE);
    boolean changed = setBrightnessConfiguration(configuration);
    changed |= setDisplayPolicy(displayPolicy);
    if (userChangedAutoBrightnessAdjustment) {
        changed |= setAutoBrightnessAdjustment(adjustment);
    }
    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();
    }
    changed |= setLightSensorEnabled(enable && !dozing);
    if (changed) {
        updateAutoBrightness(false /*sendUpdate*/);
    }
}

AutomaticBrightnessController#configure()调用来到了 DisplayPowerController #updatePowerState()

这样也知道了 newAutoBrightnessAdjustment,继续 putAutoBrightnessAdjustmentSetting:

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

就调到第 4 步 BrightnessObserver#onChange,进度条随之变化,Over!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理

    本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看

    Java技术江湖
  • 新手也能看懂的线程池学习总结

    线程池提供了一种限制和管理资源(包括执行一个任务)。每个线程池还维护一些基本统计信息,例如已完成任务的数量。

    范蠡
  • Java 8,Jenkins,Jacoco和Sonar进行持续集成

    本文的范围是解释安装和设置必要工具的所有步骤,以使Java 8的CI服务器完全正常运行。请注意,该证明已在Windows 7的开发人员机器上完成,但很容易做到。...

    八音弦
  • 用过XA分布式事务吗?

    下图说明了一个DTP系统的本地实例,其中AP调用TM来构造事务。这些框表示X/Open DTP模型中的软件组件。箭头指示控制流的方向。

    Criss@陈磊
  • 深入理解JVM虚拟机5:虚拟机字节码执行引擎

    本文转自:https://www.cnblogs.com/snailclimb/p/9086337.html

    Java技术江湖
  • 找寻gvcf失败的原因(java啊java,配置啊配置)

    因为我只想保留recal后的bam和,call出来的gvcf文件,但是发现有些样本根本就走不通这个流程,就需要debug了。

    生信技能树
  • TagLayout自定义流式布局

    这是一个继承ViewGourp来实现的自定义布局。他的核心只有一个,即当子View的宽度超出自身最大宽度时,自动换行。那么,我们先来看核心代码:

    饮水思源为名
  • Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO

    本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看

    Java技术江湖
  • 前后端分离,我怎么就选择了 Spring Boot + Vue 技术栈?

    前两天又有小伙伴私信松哥,问题还是职业规划,Java 技术栈路线这种,实际上对于这一类问题我经常不太敢回答,每个人的情况都不太一样,而小伙伴也很少详细介绍自己的...

    Java技术江湖
  • 面试官:解释一下Java字节码文件中的JVM指令

    Java 之所以流行,一个很重要的原因就是它的跨平台特性,Compile Once, Run Anywhere,编译一次,到处运行。即 Java 源码只需要编译...

    南风

扫码关注云+社区

领取腾讯云代金券