There are several common ways to adjust brightness on Android:

  • Manual brightness bar adjustment;
  • Video playing interface sliding adjustment;
  • Automatic brightness adjustment;

Among them, the first two types are manual adjustment. In the brightness adjustment process, the source and entry of brightness are different, but they will enter the DisplayPowerController to determine the final brightness. Here’s how they work.

1. Adjust the brightness manually

Manual brightness adjustment refers to the brightness adjustment achieved by dragging the brightness bar, the corresponding control is located in the SystemUI module:

frameworks/base/packages/SystemUI/src/com/android/systemui/settings/ToggleSliderView.java
frameworks/base/packages/SystemUI/src/com/android/systemui/settings/ToggleSeekBar.java
Copy the code

1.1 Sliding event response

When the brightness bar is dragged, the following methods in the corresponding ToggleSeekBar will be called back:

// 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) {
            // Pass it to mListener
            if(mListener ! =null) {
                mListener.onChanged(
                        ToggleSliderView.this, mTracking, mToggle.isChecked(), progress, false); }}@Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            // Start dragging
            mTracking = true;
            
            if(mListener ! =null) {
                mListener.onChanged(ToggleSliderView.this, mTracking, mToggle.isChecked(),
                        mSlider.getProgress(), false);
            }

            mToggle.setChecked(false); . }@Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            // Release the finger
            mTracking = false;
            if(mListener ! =null) {
                mListener.onChanged(ToggleSliderView.this, mTracking, mToggle.isChecked(),
                        mSlider.getProgress(), true); }... }};Copy the code

This passes the callback event to mListener for processing, which is an instance of BrightnessController, so the sliding event is further processed in the BrightnessController#onChanged() method:

// 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.0 f);
        // Set the brightness value
        setBrightness(valFloat);
        // When the finger is lifted, write the brightness value to your SettingsProvider
        if(! tracking) { AsyncTask.execute(new Runnable() {
                    public void run(a) { Settings.System.putFloatForUser(mContext.getContentResolver(), settingToChange, valFloat, UserHandle.USER_CURRENT); }}); }... }Copy the code

Among the above methods:

  • First of all, throughconvertGammaToLinear()Method The ToggleSeekBar progress value is converted to the corresponding brightness value.
  • And then, throughDisplayManager#setTemporaryBrightness()Methods Continuously set the brightness on the FW.
  • Finally, the brightness value in the SettingsProvider is updated through an asynchronous task after the user lifts his finger.

Since Android R, AOSP has set brightness based on floating-point types, so the resulting brightness is always a floating-point value between [0.0F, 1.0f], as discussed below.

The brightness field in the SettingsProvider is updated with Settings.System.SCREEN_BRIGHTNESS_FLOAT after the slide is complete.

In the setBrightness() method, call DMS#setTemporaryBrightness() directly:

// frameworks/base/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
    private void setBrightness(float brightness) {
        mDisplayManager.setTemporaryBrightness(brightness);
    }
Copy the code

At this point, the slide event response is complete, so let’s look at the processing of the received brightness value in the DMS.

1.2.DMS#setTemporaryBrightness() Settings brightness

MS#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); }}Copy the code

This is going to go straight to the DisplayPowerController. DisplayPowerController continues execution by Handler on the “PowerManagerService” thread:

// 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:
                    // Get the brightness value from SystemUI
                    mTemporaryScreenBrightness = Float.intBitsToFloat(msg.arg1);
                    // Update the status
                    updatePowerState();
                    break; . }}}Copy the code

Here will be floating point values assigned to the brightness from the SystemUI mTemporaryScreenBrightness variables, is named after the temporary, because once to determine the brightness, the variable’s value will be reset, the role of this value is also very important, if the value reset does not occur, Auto brightness cannot be set.

After calling updatePowerState () to update the display overall state, and set the brightness of the mTemporaryScreenBrightness as for the system to:

private void updatePowerState(a) {...// Updates the status of the manually set brightness value in the user status bar and Settings. Returns true if the user Settings brightness changes
    final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
    // This brightness represents the brightness when the user drags the brightness bar adjustment and does not let go, so it is a "temporary" brightness and must be used if it exists
    if (isValidBrightnessValue(mTemporaryScreenBrightness)) {
        brightnessState = mTemporaryScreenBrightness;
        // Indicates temporary user brightness is used
        mAppliedTemporaryBrightness = true;   
        mBrightnessReasonTemp.setReason(BrightnessReason.REASON_TEMPORARY);
    } else {
        mAppliedTemporaryBrightness = false; }... }Copy the code

DisplayPowerController#updatePowerState() is detailed in the Android R DisplayManagerService module (3).

2. Adjust the brightness of the video interface

We are familiar with the scene of video interface brightness adjustment, which is achieved by setting properties for the window. When to window set WindowManger. LayoutParams. ScreenBrightnesss properties after entering the window will adjust brightness.

Window traversal in THE WMS module will judge the property:

// 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 booleancanBeSeen = w.isDisplayedLw(); .if (w.mHasSurface && canBeSeen) {
            ......
            if(! syswin && w.mAttrs.screenBrightness >=0&& Float.isNaN(mScreenBrightnessOverride)) { mScreenBrightnessOverride = w.mAttrs.screenBrightness; }... }return displayHasContent;
    }
Copy the code

Then set the brightness to PMS after placing the window Surface:

    void performSurfacePlacementNoTrace(a) {...if(! mWmService.mDisplayFrozen) {// Verify the validity
            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(); }}Copy the code

Through PMS# setScreenBrightnessOverrideFromWindowManager brightness () method will be sent to the PMS:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

    private void setScreenBrightnessOverrideFromWindowManagerInternal(float brightness) {
        synchronized (mLock) {
            if(! BrightnessSynchronizer.floatEquals(mScreenBrightnessOverrideFromWindowManager, brightness)) {/ / set to global variables mScreenBrightnessOverrideFromWindowManagermScreenBrightnessOverrideFromWindowManager = brightness; mDirty |= DIRTY_SETTINGS; updatePowerStateLocked(); }}}Copy the code

After receiving the PMS, will the brightness as DisplayPowerRequest. ScreenBrightnessOverride attribute values, launched an request, the DMS module in DisplayPowerController processing request, then set the brightness to the global brightness.


private void updatePowerState(a) {...// Use overlay brightness in PMS
    if ((Float.isNaN(brightnessState))
                && isValidBrightnessValue(mPowerRequest.screenBrightnessOverride)) {  
        brightnessState = mPowerRequest.screenBrightnessOverride;
        // Record the cause of brightness change
        mBrightnessReasonTemp.setReason(BrightnessReason.REASON_OVERRIDE);  
        // indicates that overlay brightness is used
        mAppliedScreenBrightnessOverride = true;   
    } else {
        mAppliedScreenBrightnessOverride = false; }... }Copy the code

From the order in which the brightness is set in the DisplayPowerController#updatePowerState() method, the overwrite brightness from the window is the highest priority.

3. Floating point brightness Settings for Android R

On Android R, the biggest brightness difference is floatation, changing the original brightness range from 0 to 255 to 0.0 to 1.0f. However, in order to be compatible with the old scheme, the existing Int brightness will be converted to the corresponding Float brightness, according to the original Int value Float to the range of [0.0f, 1.0f] values.

3.1. Added configuration parameters

On Android R, the following configuration values are added:

<! -- frameworks/base/core/res/res/values/config.xml -->
	<! -- Minimum brightness -->
    <item name="config_screenBrightnessSettingMinimumFloat" format="float" type="dimen">2 -</item>
	<! -- Maximum brightness -->
    <item name="config_screenBrightnessSettingMaximumFloat" format="float" type="dimen">2 -</item>
	<! -- Default brightness -->
    <item name="config_screenBrightnessSettingDefaultFloat" format="float" type="dimen">2 -</item>
	<! -- Doze default brightness -->
    <item name="config_screenBrightnessDozeFloat" format="float" type="dimen">0.0</item>
	<! -- Dim -->
    <item name="config_screenBrightnessDimFloat" format="float" type="dimen">0.05</item>
Copy the code

These values correspond to the same configuration on the Q version:

<! -- 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>
Copy the code

Therefore, on R, the configuration should take precedence over the Float configuration. If a Float is not configured, the Int configuration parameter is converted to a Float configuration.

3.2. Conversion between old scheme (Int) and new scheme (Float)

In PowerManagerService, the above new configuration parameters are read:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

@VisibleForTesting
    PowerManagerService(Context context, Injector injector) {

		.........
		// Reads the Float configuration parameters
        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);

		// Convert Int -> Float
        if (min == INVALID_BRIGHTNESS_IN_CONFIG || max == INVALID_BRIGHTNESS_IN_CONFIG
                || def == INVALID_BRIGHTNESS_IN_CONFIG) {
			// Minimum brightness conversion
            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);
			// Maximum brightness conversion
            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);
			// Default brightness conversion
            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; }... }Copy the code

Float Float Float Float Float Float Float Float Float Float Float Float Float Float Float Float Float Float Float Float Float Float Float Float Float Float Float

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

    private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f;
Copy the code

Otherwise, an Int → Float conversion is performed, converting an Int parameter configured in the old scheme to a Float parameter.

The BrightnessSynchronizer class is specifically used to convert Int and Float brightness configuration parameters to and from each other. Turn such as Int, Float, through BrightnessSynchronizer. BrightnessIntToFloat () method.

3.3. The new API

Android on R, though retained PowerManager acquiring original brightness in the API, such as getMinimumScreenBrightnessSetting () interface, but almost can’t find their use. Instead, the new PowerManager#getBrightnessConstraint(int) interface is used to obtain brightness configuration values for other modules and is qualified by parameters. Parameters are also defined in PowerManager:

// frameworks/base/core/java/android/os/PowerManager.java

    public static final int BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM = 0;	// Minimum brightness
    public static final int BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM = 1;	// Maximum brightness
    public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT = 2;	// Default brightness
    public static final int BRIGHTNESS_CONSTRAINT_TYPE_DIM = 3;		/ /, DIM brightness
    public static final int BRIGHTNESS_CONSTRAINT_TYPE_DOZE = 4;	// Doze defaults to brightness
    public static final int BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR = 5;	// Minimum brightness in VR mode
    public static final int BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR = 6;	// Maximum brightness in VR mode
    public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR = 7;	// Default brightness in VR mode
Copy the code

In addition, several new constants have been added to PowerManager:

// frameworks/base/core/java/android/os/PowerManager.java
	// Q is defined on
    public static final int BRIGHTNESS_ON = 255;
    public static final int BRIGHTNESS_OFF = 0;
    public static final int BRIGHTNESS_DEFAULT = -1;

 	// R adds a constant
    public static final int BRIGHTNESS_INVALID = -1;
    public static final float BRIGHTNESS_MAX = 1.0 f;
    public static final float BRIGHTNESS_MIN = 0.0 f;
    public static final float BRIGHTNESS_OFF_FLOAT = -1.0 f;
    public static final float BRIGHTNESS_INVALID_FLOAT = Float.NaN;
Copy the code

PowerManager#getBrightnessConstraint(int)

// frameworks/base/core/java/android/os/PowerManager.java

    public float getBrightnessConstraint(int constraint) {
        try {
            return mService.getBrightnessConstraint(constraint);
        } catch (RemoteException e) {
            throwe.rethrowFromSystemServer(); }}Copy the code

This method is called directly into the PMS with Binder:

// 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:
                    returnPowerManager.BRIGHTNESS_INVALID_FLOAT; }}Copy the code

For example, in the status bar, this interface is used to get the maximum and minimum brightness and limit the brightness:

// frameworks/base/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java

// Get the maximum and minimum brightness values
mMinimumBacklight = pm.getBrightnessConstraint(
        PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
mMaximumBacklight = pm.getBrightnessConstraint(
        PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
// Calculate float brightness based on progress bar values
final float valFloat = MathUtils.min(convertGammaToLinearFloat(value,
        minBacklight, maxBacklight), 1.0 f);

// Set brightness
private void setBrightness(float brightness) {
    mDisplayManager.setTemporaryBrightness(brightness);
}
Copy the code

3.4. Internal interface Float

Do you want to Float all the options that have related parameters? For example, the DisplayManager#setTemporaryBrightness(Float) method? Involved within the framework class DisplayPowerController, DisplayPowerState, LocalDisplayAdapter, AutomaticBrighnessController etc., Both convert the int argument involved in the original method to Float. I won’t list them all here.

3.5. Changes in AutomaticBrightnessController

There’s not a lot of change in auto brightness, because auto brightness was Float before R, and both the configuration curve and the auto brightness were Float, but when it was passed to the DisplayPowerController, it had an Int conversion. R removes this conversion and uses it directly:

// frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java

    private void updateAutoBrightness(boolean sendUpdate, boolean isManuallySet) {
        if(! mAmbientLuxValid) {return;
        }
		// The brightness of the range [0.0, 1.0] is obtained
        float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
                mForegroundAppCategory);
		// Instead of converting, set float
        float newScreenAutoBrightness = clampScreenBrightness(value);
        
        if(! BrightnessSynchronizer.floatEquals(mScreenAutoBrightness, newScreenAutoBrightness)) { .......if(sendUpdate) { mCallbacks.updateBrightness(); }}}Copy the code

3.6. Add a new field to SettingsProvider

On Android R, the brightness value is saved in settings.system.SCREEN_BRIGHTNESS field. On R, this field is also reserved, but because of Float, Settings.system. SCREEN_BRIGHTNESS_FLOAT is also added to the corresponding Float value, which is updated first when the brightness changes:

// 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);
    }
Copy the code

To maintain consistency between the two fields, the old field is updated when the value of the field changes, which is also done in the 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); }}Copy the code

Similarly, if the value of the int field changes, the value in the new field changes as well, updating the brightness.