专栏首页吴小龙同學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 条评论
登录 后参与评论

相关文章

  • Android 9.0 SystemUI 下拉状态栏快捷开关

    SystemUI 下拉状态栏快捷开关是 QSPanel,qs_panel.xml,@+id/quick_settings_panel,本篇文章就来看看这些快捷开...

    吴小龙同學
  • Android 9.0 SystemUI Notification

    本文主要分享 SystemUI Notification 具体如何呈现的?基于 AOSP 9.0 分析。

    吴小龙同學
  • Android Studio 导入 AOSP 源码

    有了 AOSP 源码,接下来就是如何看了,可以直接文本看,可以用 Source Insight,我当然选择 Android Studio,Android Stu...

    吴小龙同學
  • 异步编程中使用帮助类来实现Thread.Start()的示例

    1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 ...

    CNXY
  • CryptoKitties源码详解:手把手教你撸出自己的区块链游戏

    区块链大本营
  • Linq快速入门——Lambda表达式的前世今生

    Lambda表达式其实并不陌生,他的前生就是匿名函数,所以要谈Lambda表达式,就不得不谈匿名函数,要谈匿名函数,那又要不得不谈委托。 何为委托 匿名方法 ...

    用户1161731
  • 设计师规划好你的2017了么?

    元宵已经过去了,2016新年已经彻底过完了。说过的快的人一般分两种:第一种无所事事,大量做重复的工作,看起来很忙但是没卵用,日子过的很快。第二种是几乎每天作总结...

    宇相
  • crontab并发文件锁的使用

    前几天在做任务时间调度的时候,写了一个Shell脚本,是通过脚本来操作corntab的配置,在修改之前会做备份,文件是crontab_bak_file,然后...

    jeanron100
  • 官方mycat安装入门教程

    cd /usr/local/mycat/conf vim wrapper.conf

    林老师带你学编程
  • MySQL 8 OCP(1Z0-908)认证考试题库原题(第10题)

    Examine these commands, which execute successfully on the ic1 host:

    用户5892232

扫码关注云+社区

领取腾讯云代金券