Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c65d8403 authored by Manali Bhutiyani's avatar Manali Bhutiyani
Browse files

[power-throttling] Fine tune algorithm

This change fine-tunes the power-based throttling algorithm to
take into account the current brightness instead of max brightness for the
Throttling limit.
It also fixes underutilzation of power and resets the pmic handler,
if thermal state is 0.

Test: Manual and updated atests
       atest DisplayServiceTests
       atest com.android.server.display.brightness.clamper.BrightnessPowerClamperTest
Flag: com.android.server.display.feature.flags.enable_power_throttling_clamper
Bug: 302814899
Change-Id: I754c86c26d51fde1ccd6c91ba649f58fc73afcd2
parent 10ff8b86
Loading
Loading
Loading
Loading
+23 −8
Original line number Diff line number Diff line
@@ -150,7 +150,9 @@ import javax.xml.datatype.DatatypeConfigurationException;
 *      <screenBrightnessDefault>0.65</screenBrightnessDefault>
 *      <powerThrottlingConfig>
 *        <brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>
 *        <pollingWindowMillis>15</pollingWindowMillis>
 *        <customAnimationRateSec>0.004</customAnimationRateSec>
 *        <pollingWindowMaxMillis>30000</pollingWindowMaxMillis>
 *        <pollingWindowMinMillis>10000</pollingWindowMinMillis>
 *          <powerThrottlingMap>
 *              <powerThrottlingPoint>
 *                  <thermalStatus>severe</thermalStatus>
@@ -2184,9 +2186,13 @@ public class DisplayDeviceConfig {
            return;
        }
        float lowestBrightnessCap = powerThrottlingCfg.getBrightnessLowestCapAllowed().floatValue();
        int pollingWindowMillis = powerThrottlingCfg.getPollingWindowMillis().intValue();
        float customAnimationRateSec = powerThrottlingCfg.getCustomAnimationRateSec().floatValue();
        int pollingWindowMaxMillis = powerThrottlingCfg.getPollingWindowMaxMillis().intValue();
        int pollingWindowMinMillis = powerThrottlingCfg.getPollingWindowMinMillis().intValue();
        mPowerThrottlingConfigData = new PowerThrottlingConfigData(lowestBrightnessCap,
                                                                   pollingWindowMillis);
                                                                   customAnimationRateSec,
                                                                   pollingWindowMaxMillis,
                                                                   pollingWindowMinMillis);
    }

    private void loadRefreshRateSetting(DisplayConfiguration config) {
@@ -2980,12 +2986,19 @@ public class DisplayDeviceConfig {
    public static class PowerThrottlingConfigData {
        /** Lowest brightness cap allowed for this device. */
        public final float brightnessLowestCapAllowed;
        /** Time window for polling power in seconds. */
        public final int pollingWindowMillis;
        /** Time take to animate brightness in seconds. */
        public final float customAnimationRateSec;
        /** Time window for maximum polling power in milliseconds. */
        public final int pollingWindowMaxMillis;
        /** Time window for minimum polling power in milliseconds. */
        public final int pollingWindowMinMillis;
        public PowerThrottlingConfigData(float brightnessLowestCapAllowed,
                int pollingWindowMillis) {
                float customAnimationRateSec, int pollingWindowMaxMillis,
                int pollingWindowMinMillis) {
            this.brightnessLowestCapAllowed = brightnessLowestCapAllowed;
            this.pollingWindowMillis = pollingWindowMillis;
            this.customAnimationRateSec = customAnimationRateSec;
            this.pollingWindowMaxMillis = pollingWindowMaxMillis;
            this.pollingWindowMinMillis = pollingWindowMinMillis;
        }

        @Override
@@ -2993,7 +3006,9 @@ public class DisplayDeviceConfig {
            return "PowerThrottlingConfigData{"
                    + "brightnessLowestCapAllowed: "
                    + brightnessLowestCapAllowed
                    + ", pollingWindowMillis: " + pollingWindowMillis
                    + ", customAnimationRateSec: " + customAnimationRateSec
                    + ", pollingWindowMaxMillis: " + pollingWindowMaxMillis
                    + ", pollingWindowMinMillis: " + pollingWindowMinMillis
                    + "} ";
        }
    }
+4 −3
Original line number Diff line number Diff line
@@ -591,7 +591,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
                        mThermalBrightnessThrottlingDataId,
                        logicalDisplay.getPowerThrottlingDataIdLocked(),
                        mDisplayDeviceConfig, displayDeviceInfo.width, displayDeviceInfo.height,
                        displayToken, mDisplayId), mContext, flags, mSensorManager);
                        displayToken, mDisplayId), mContext, flags, mSensorManager,
                        mDisplayBrightnessController.getCurrentBrightness());
        // Seed the cached brightness
        saveBrightnessInfo(getScreenBrightnessSetting());
        mAutomaticBrightnessStrategy =
@@ -3300,10 +3301,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
        BrightnessClamperController getBrightnessClamperController(Handler handler,
                BrightnessClamperController.ClamperChangeListener clamperChangeListener,
                BrightnessClamperController.DisplayDeviceData data, Context context,
                DisplayManagerFlags flags, SensorManager sensorManager) {
                DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {

            return new BrightnessClamperController(handler, clamperChangeListener, data, context,
                    flags, sensorManager);
                    flags, sensorManager, currentBrightness);
        }

        DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
+30 −7
Original line number Diff line number Diff line
@@ -75,10 +75,13 @@ public class BrightnessClamperController {
    private ModifiersAggregatedState mModifiersAggregatedState = new ModifiersAggregatedState();

    private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener;
    private final DisplayManagerFlags mDisplayManagerFlags;
    private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;

    private float mCustomAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
    @Nullable
    private BrightnessPowerClamper mPowerClamper;
    @Nullable
    private Type mClamperType = null;

    private boolean mClamperApplied = false;
@@ -93,16 +96,18 @@ public class BrightnessClamperController {

    public BrightnessClamperController(Handler handler,
            ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
            DisplayManagerFlags flags, SensorManager sensorManager) {
        this(new Injector(), handler, clamperChangeListener, data, context, flags, sensorManager);
            DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
        this(new Injector(), handler, clamperChangeListener, data, context, flags, sensorManager,
                currentBrightness);
    }

    @VisibleForTesting
    BrightnessClamperController(Injector injector, Handler handler,
            ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
            DisplayManagerFlags flags, SensorManager sensorManager) {
            DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
        mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider();
        mHandler = handler;
        mDisplayManagerFlags = flags;
        mLightSensorController = injector.getLightSensorController(sensorManager, context,
                mLightSensorListener, mHandler);

@@ -117,7 +122,15 @@ public class BrightnessClamperController {
        };

        mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags,
                context);
                context, currentBrightness);
        if (mDisplayManagerFlags.isPowerThrottlingClamperEnabled()) {
            for (BrightnessClamper clamper: mClampers) {
                if (clamper.getType() == Type.POWER) {
                    mPowerClamper = (BrightnessPowerClamper) clamper;
                    break;
                }
            }
        }
        mModifiers = injector.getModifiers(flags, context, handler, clamperChangeListener,
                data);

@@ -183,6 +196,12 @@ public class BrightnessClamperController {
            mModifiers.get(i).apply(request, builder);
        }

        if (mDisplayManagerFlags.isPowerThrottlingClamperEnabled()) {
            if (mPowerClamper != null) {
                mPowerClamper.updateCurrentBrightness(cappedBrightness);
            }
        }

        return builder.build();
    }

@@ -311,13 +330,17 @@ public class BrightnessClamperController {

        List<BrightnessClamper<? super DisplayDeviceData>> getClampers(Handler handler,
                ClamperChangeListener clamperChangeListener, DisplayDeviceData data,
                DisplayManagerFlags flags, Context context) {
                DisplayManagerFlags flags, Context context, float currentBrightness) {
            List<BrightnessClamper<? super DisplayDeviceData>> clampers = new ArrayList<>();
            clampers.add(
                    new BrightnessThermalClamper(handler, clamperChangeListener, data));
            if (flags.isPowerThrottlingClamperEnabled()) {
                // Check if power-throttling config is present.
                PowerThrottlingConfigData configData = data.getPowerThrottlingConfigData();
                if (configData != null) {
                    clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
                        data));
                            data, currentBrightness));
                }
            }
            if (flags.isBrightnessWearBedtimeModeClamperEnabled()) {
                clampers.add(new BrightnessWearBedtimeModeClamper(handler, context,
+209 −28
Original line number Diff line number Diff line
@@ -21,16 +21,23 @@ import static com.android.server.display.brightness.clamper.BrightnessClamperCon

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Handler;
import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.Temperature;
import android.provider.DeviceConfigInterface;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData;
import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel;
import com.android.server.display.brightness.BrightnessUtils;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.DeviceConfigParsingUtils;

@@ -65,14 +72,21 @@ class BrightnessPowerClamper extends
    private PowerThrottlingData mPowerThrottlingDataActive = null;
    @Nullable
    private PowerThrottlingConfigData mPowerThrottlingConfigData = null;

    @NonNull
    private final ThermalLevelListener mThermalLevelListener;
    @NonNull
    private final PowerChangeListener mPowerChangeListener;
    private @Temperature.ThrottlingStatus int mCurrentThermalLevel = Temperature.THROTTLING_NONE;
    private boolean mCurrentThermalLevelChanged = false;
    private float mCurrentAvgPowerConsumed = 0;
    @Nullable
    private String mUniqueDisplayId = null;
    @Nullable
    private String mDataId = null;

    private float mCurrentBrightness = PowerManager.BRIGHTNESS_INVALID;
    private float mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
    private float mCustomAnimationRateSecDeviceConfig =
                        DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
    private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
        try {
            int status = DeviceConfigParsingUtils.parseThermalStatus(key);
@@ -88,23 +102,41 @@ class BrightnessPowerClamper extends


    BrightnessPowerClamper(Handler handler, ClamperChangeListener listener,
            PowerData powerData) {
        this(new Injector(), handler, listener, powerData);
            PowerData powerData, float currentBrightness) {
        this(new Injector(), handler, listener, powerData, currentBrightness);
    }

    @VisibleForTesting
    BrightnessPowerClamper(Injector injector, Handler handler, ClamperChangeListener listener,
            PowerData powerData) {
                           PowerData powerData, float currentBrightness) {
        super(handler, listener);
        mInjector = injector;
        mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
        mCurrentBrightness = currentBrightness;
        mPowerChangeListener = (powerConsumed, thermalStatus) -> {
            recalculatePowerQuotaChange(powerConsumed, thermalStatus);
        };
        mPowerThrottlingConfigData = powerData.getPowerThrottlingConfigData();
        if (mPowerThrottlingConfigData != null) {
            mCustomAnimationRateSecDeviceConfig = mPowerThrottlingConfigData.customAnimationRateSec;
        }
        mThermalLevelListener = new ThermalLevelListener(handler);
        mPmicMonitor =
            mInjector.getPmicMonitor(mPowerChangeListener,
                    mThermalLevelListener.getThermalService(),
                    mPowerThrottlingConfigData.pollingWindowMaxMillis,
                    mPowerThrottlingConfigData.pollingWindowMinMillis);

        mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
        mHandler.post(() -> {
            setDisplayData(powerData);
            loadOverrideData();
            start();
        });
    }

    @VisibleForTesting
    PowerChangeListener getPowerChangeListener() {
        return mPowerChangeListener;
    }

    @Override
@@ -113,6 +145,11 @@ class BrightnessPowerClamper extends
        return Type.POWER;
    }

    @Override
    float getCustomAnimationRate() {
        return mCustomAnimationRateSec;
    }

    @Override
    void onDeviceConfigChanged() {
        mHandler.post(() -> {
@@ -134,6 +171,9 @@ class BrightnessPowerClamper extends
        if (mPmicMonitor != null) {
            mPmicMonitor.shutdown();
        }
        if (mThermalLevelListener != null) {
            mThermalLevelListener.stop();
        }
    }

    /**
@@ -144,11 +184,20 @@ class BrightnessPowerClamper extends
        pw.println("  mCurrentAvgPowerConsumed=" + mCurrentAvgPowerConsumed);
        pw.println("  mUniqueDisplayId=" + mUniqueDisplayId);
        pw.println("  mCurrentThermalLevel=" + mCurrentThermalLevel);
        pw.println("  mCurrentThermalLevelChanged=" + mCurrentThermalLevelChanged);
        pw.println("  mPowerThrottlingDataFromDDC=" + (mPowerThrottlingDataFromDDC == null ? "null"
                : mPowerThrottlingDataFromDDC.toString()));
        mThermalLevelListener.dump(pw);
        super.dump(pw);
    }

    /**
     * Updates current brightness, for power calculations.
     */
    public void updateCurrentBrightness(float currentBrightness) {
        mCurrentBrightness = currentBrightness;
    }

    private void recalculateActiveData() {
        if (mUniqueDisplayId == null || mDataId == null) {
            return;
@@ -156,17 +205,11 @@ class BrightnessPowerClamper extends
        mPowerThrottlingDataActive = mPowerThrottlingDataOverride
                .getOrDefault(mUniqueDisplayId, Map.of()).getOrDefault(mDataId,
                        mPowerThrottlingDataFromDDC);
        if (mPowerThrottlingDataActive != null) {
            if (mPmicMonitor != null) {
                mPmicMonitor.stop();
                mPmicMonitor.start();
            }
        } else {
        if (mPowerThrottlingDataActive == null) {
            if (mPmicMonitor != null) {
                mPmicMonitor.stop();
            }
        }
        recalculateBrightnessCap();
    }

    private void loadOverrideData() {
@@ -198,21 +241,57 @@ class BrightnessPowerClamper extends
        if (mPowerThrottlingDataActive == null) {
            return;
        }
        if (powerQuota > 0 && mCurrentAvgPowerConsumed > powerQuota) {
        if (powerQuota > 0) {
            if (BrightnessUtils.isValidBrightnessValue(mCurrentBrightness)
                    && (mCurrentAvgPowerConsumed > powerQuota)) {
                isActive = true;
                // calculate new brightness Cap.
                // Brightness has a linear relation to power-consumed.
                targetBrightnessCap =
                    (powerQuota / mCurrentAvgPowerConsumed) * PowerManager.BRIGHTNESS_MAX;
                    (powerQuota / mCurrentAvgPowerConsumed) * mCurrentBrightness;
            } else if (mCurrentThermalLevelChanged) {
                if (mCurrentThermalLevel == Temperature.THROTTLING_NONE) {
                    // reset pmic and remove the power-throttling cap.
                    isActive = true;
                    targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
                    mPmicMonitor.stop();
                } else {
                    isActive = true;
                    // Since the thermal status has changed, we need to remove power-throttling cap.
                    // Instead of recalculating and changing brightness again, adding flicker,
                    // we will wait for the next pmic cycle to re-evaluate this value
                    // make act on it, if needed.
                    targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
                    if (mPmicMonitor.isStopped()) {
                        mPmicMonitor.start();
                    }
                }
            } else { // Current power consumed is under the quota.
                isActive = true;
                targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
            }
        }

        // Cap to lowest allowed brightness on device.
        if (mPowerThrottlingConfigData != null) {
            targetBrightnessCap = Math.max(targetBrightnessCap,
                                mPowerThrottlingConfigData.brightnessLowestCapAllowed);
        }

        if (mBrightnessCap != targetBrightnessCap || mIsActive != isActive) {
            mIsActive = isActive;
            Slog.i(TAG, "Power clamper changing current brightness cap mBrightnessCap: "
                    + mBrightnessCap + " to target brightness cap:" + targetBrightnessCap
                    + " for current screen brightness: " + mCurrentBrightness);
            mBrightnessCap = targetBrightnessCap;
            Slog.i(TAG, "Power clamper changed state: thermalStatus:" + mCurrentThermalLevel
                    + " mCurrentThermalLevelChanged:" + mCurrentThermalLevelChanged
                    + " mCurrentAvgPowerConsumed:" + mCurrentAvgPowerConsumed
                    + " mCustomAnimationRateSec:" + mCustomAnimationRateSecDeviceConfig);
            mCustomAnimationRateSec = mCustomAnimationRateSecDeviceConfig;
            mChangeListener.onChanged();
        } else {
            mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
        }
    }

@@ -234,6 +313,11 @@ class BrightnessPowerClamper extends

    private void recalculatePowerQuotaChange(float avgPowerConsumed, int thermalStatus) {
        mHandler.post(() -> {
            if (mCurrentThermalLevel != thermalStatus) {
                mCurrentThermalLevelChanged = true;
            } else {
                mCurrentThermalLevelChanged = false;
            }
            mCurrentThermalLevel = thermalStatus;
            mCurrentAvgPowerConsumed = avgPowerConsumed;
            recalculateBrightnessCap();
@@ -244,14 +328,107 @@ class BrightnessPowerClamper extends
        if (mPowerThrottlingConfigData == null) {
            return;
        }
        PowerChangeListener listener = (powerConsumed, thermalStatus) -> {
            recalculatePowerQuotaChange(powerConsumed, thermalStatus);
        };
        mPmicMonitor =
            mInjector.getPmicMonitor(listener, mPowerThrottlingConfigData.pollingWindowMillis);
        if (mPowerThrottlingConfigData.pollingWindowMaxMillis
                <= mPowerThrottlingConfigData.pollingWindowMinMillis) {
            Slog.e(TAG, "Brightness power max polling window:"
                    + mPowerThrottlingConfigData.pollingWindowMaxMillis
                    + " msec, should be greater than brightness min polling window:"
                    + mPowerThrottlingConfigData.pollingWindowMinMillis + " msec.");
            return;
        }
        if ((mPowerThrottlingConfigData.pollingWindowMaxMillis
                % mPowerThrottlingConfigData.pollingWindowMinMillis) != 0) {
            Slog.e(TAG, "Brightness power max polling window:"
                    + mPowerThrottlingConfigData.pollingWindowMaxMillis
                    + " msec, is not divisible by brightness min polling window:"
                    + mPowerThrottlingConfigData.pollingWindowMinMillis + " msec.");
            return;
        }
        mCustomAnimationRateSecDeviceConfig = mPowerThrottlingConfigData.customAnimationRateSec;
        mThermalLevelListener.start();
    }

    private void activatePmicMonitor() {
        if (!mPmicMonitor.isStopped()) {
            return;
        }
        mPmicMonitor.start();
    }

    private void deactivatePmicMonitor(@Temperature.ThrottlingStatus int status) {
        if (status != Temperature.THROTTLING_NONE) {
            return;
        }
        if (mPmicMonitor.isStopped()) {
            return;
        }
        mPmicMonitor.stop();
    }

    private final class ThermalLevelListener extends IThermalEventListener.Stub {
        private final Handler mHandler;
        private IThermalService mThermalService;
        private boolean mStarted;

        ThermalLevelListener(Handler handler) {
            mHandler = handler;
            mStarted = false;
            mThermalService = IThermalService.Stub.asInterface(
                    ServiceManager.getService(Context.THERMAL_SERVICE));
        }

        IThermalService getThermalService() {
            return mThermalService;
        }

        void start() {
            if (mStarted) {
                return;
            }
            if (mThermalService == null) {
                return;
            }
            try {
                // TODO b/279114539 Try DISPLAY first and then fallback to SKIN.
                mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
                mStarted = true;
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to register thermal status listener", e);
            }
        }

        @Override
        public void notifyThrottling(Temperature temp) {
            @Temperature.ThrottlingStatus int status = temp.getStatus();
            if (status >= Temperature.THROTTLING_LIGHT) {
                Slog.d(TAG, "Activating pmic monitor due to thermal state:" + status);
                mHandler.post(() -> activatePmicMonitor());
            } else {
                if (!mPmicMonitor.isStopped()) {
                    mHandler.post(() -> deactivatePmicMonitor(status));
                }
            }
        }

        void stop() {
            if (!mStarted) {
                return;
            }
            try {
                mThermalService.unregisterThermalEventListener(this);
                mStarted = false;
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to unregister thermal status listener", e);
            }
            mThermalService = null;
        }

        void dump(PrintWriter writer) {
            writer.println("  ThermalLevelObserver:");
            writer.println("    mStarted: " + mStarted);
        }
    }

    public interface PowerData {
        @NonNull
        String getUniqueDisplayId();
@@ -279,8 +456,12 @@ class BrightnessPowerClamper extends

    @VisibleForTesting
    static class Injector {
        PmicMonitor getPmicMonitor(PowerChangeListener listener, int pollingTime) {
            return new PmicMonitor(listener, pollingTime);
        PmicMonitor getPmicMonitor(PowerChangeListener powerChangeListener,
                                   IThermalService thermalService,
                                   int pollingMaxTimeMillis,
                                   int pollingMinTimeMillis) {
            return new PmicMonitor(powerChangeListener, thermalService, pollingMaxTimeMillis,
                                        pollingMinTimeMillis);
        }

        DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
+54 −19

File changed.

Preview size limit exceeded, changes collapsed.

Loading