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

Commit 5ad9ca63 authored by Manali Bhutiyani's avatar Manali Bhutiyani
Browse files

[hbm] Separate HBM times for multi-display devices.

This change separates High brightness mode information
for different physical displays, which was currently being shared
causing both displays to show similar HBM time remaining.
Each display's OLED lifetime risk due to HBM is independent,
so each display's HBM for SV timer should be independent.

Bug: 254588984

Test: atest com.android.server.display.HbmEventTest
      atest com.android.server.display.HighBrightnessModeControllerTest
      atest com.android.server.display.HighBrightnessModeMetadataTest
      atest com.android.server.display.DisplayPowerControllerTest
      atest com.android.server.display.DisplayPowerControllerTest2

      Manual testing:
      Manually tested this fix on multiple display devices, with high lux and
      forcing displays into HBM mode, and changing the
      displays to see if the time events are being updated correctly.

      $ adb shell dumpsys display | grep -i remainingtime
      remainingTime=274576
      remainingTime=249182

Change-Id: I0ed4ad99108a1a850dae68e1f6cf92adfab8bc1d
parent 7fd018f3
Loading
Loading
Loading
Loading
+60 −4
Original line number Diff line number Diff line
@@ -108,6 +108,7 @@ import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IntArray;
@@ -259,6 +260,13 @@ public final class DisplayManagerService extends SystemService {
    final SparseArray<Pair<IVirtualDevice, DisplayWindowPolicyController>>
            mDisplayWindowPolicyControllers = new SparseArray<>();

    /**
     *  Map of every internal primary display device {@link HighBrightnessModeMetadata}s indexed by
     *  {@link DisplayDevice#mUniqueId}.
     */
    public final ArrayMap<String, HighBrightnessModeMetadata> mHighBrightnessModeMetadataMap =
            new ArrayMap<>();

    // List of all currently registered display adapters.
    private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();

@@ -1634,7 +1642,16 @@ public final class DisplayManagerService extends SystemService {

        DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
        if (dpc != null) {
            dpc.onDisplayChanged();
            final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
            if (device == null) {
                Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
                        + display.getDisplayIdLocked());
                return;
            }

            final String uniqueId = device.getUniqueId();
            HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
            dpc.onDisplayChanged(hbmMetadata);
        }
    }

@@ -1692,7 +1709,15 @@ public final class DisplayManagerService extends SystemService {
        final int displayId = display.getDisplayIdLocked();
        final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
        if (dpc != null) {
            dpc.onDisplayChanged();
            final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
            if (device == null) {
                Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
                        + display.getDisplayIdLocked());
                return;
            }
            final String uniqueId = device.getUniqueId();
            HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
            dpc.onDisplayChanged(hbmMetadata);
        }
    }

@@ -2645,6 +2670,31 @@ public final class DisplayManagerService extends SystemService {
        mLogicalDisplayMapper.forEachLocked(this::addDisplayPowerControllerLocked);
    }

    private HighBrightnessModeMetadata getHighBrightnessModeMetadata(LogicalDisplay display) {
        final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
        if (device == null) {
            Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: "
                    + display.getDisplayIdLocked());
            return null;
        }

        // HBM brightness mode is only applicable to internal physical displays.
        if (display.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
            return null;
        }

        final String uniqueId = device.getUniqueId();

        if (mHighBrightnessModeMetadataMap.containsKey(uniqueId)) {
            return mHighBrightnessModeMetadataMap.get(uniqueId);
        }

        // HBM Time info not present. Create a new one for this physical display.
        HighBrightnessModeMetadata hbmInfo = new HighBrightnessModeMetadata();
        mHighBrightnessModeMetadataMap.put(uniqueId, hbmInfo);
        return hbmInfo;
    }

    @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
    private void addDisplayPowerControllerLocked(LogicalDisplay display) {
        if (mPowerHandler == null) {
@@ -2660,17 +2710,23 @@ public final class DisplayManagerService extends SystemService {
                display, mSyncRoot);
        final DisplayPowerControllerInterface displayPowerController;

        // If display is internal and has a HighBrightnessModeMetadata mapping, use that.
        // Or create a new one and use that.
        // We also need to pass a mapping of the HighBrightnessModeTimeInfoMap to
        // displayPowerController, so the hbm info can be correctly associated
        // with the corresponding displaydevice.
        HighBrightnessModeMetadata hbmMetadata = getHighBrightnessModeMetadata(display);
        if (DeviceConfig.getBoolean("display_manager",
                "use_newly_structured_display_power_controller", true)) {
            displayPowerController = new DisplayPowerController2(
                    mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
                    mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
                    () -> handleBrightnessChange(display));
                    () -> handleBrightnessChange(display), hbmMetadata);
        } else {
            displayPowerController = new DisplayPowerController(
                    mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
                    mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
                    () -> handleBrightnessChange(display));
                    () -> handleBrightnessChange(display), hbmMetadata);
        }
        mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
    }
+9 −5
Original line number Diff line number Diff line
@@ -388,6 +388,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
    private float[] mNitsRange;

    private final HighBrightnessModeController mHbmController;
    private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;

    private final BrightnessThrottler mBrightnessThrottler;

@@ -505,13 +506,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
            DisplayPowerCallbacks callbacks, Handler handler,
            SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
            BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
            Runnable onBrightnessChangeRunnable) {
            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {

        mInjector = injector != null ? injector : new Injector();
        mClock = mInjector.getClock();
        mLogicalDisplay = logicalDisplay;
        mDisplayId = mLogicalDisplay.getDisplayIdLocked();
        mTag = "DisplayPowerController[" + mDisplayId + "]";
        mHighBrightnessModeMetadata = hbmMetadata;
        mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
        mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
        mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
@@ -790,7 +792,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
     * Make sure DisplayManagerService.mSyncRoot is held when this is called
     */
    @Override
    public void onDisplayChanged() {
    public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata) {
        final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
        if (device == null) {
            Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: "
@@ -812,7 +814,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
                mUniqueDisplayId = uniqueId;
                mDisplayStatsId = mUniqueDisplayId.hashCode();
                mDisplayDeviceConfig = config;
                loadFromDisplayDeviceConfig(token, info);
                loadFromDisplayDeviceConfig(token, info, hbmMetadata);

                // Since the underlying display-device changed, we really don't know the
                // last command that was sent to change it's state. Lets assume it is off and we
@@ -864,7 +866,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
        }
    }

    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
                                             HighBrightnessModeMetadata hbmMetadata) {
        // All properties that depend on the associated DisplayDevice and the DDC must be
        // updated here.
        loadBrightnessRampRates();
@@ -877,6 +880,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
                    mBrightnessRampIncreaseMaxTimeMillis,
                    mBrightnessRampDecreaseMaxTimeMillis);
        }
        mHbmController.setHighBrightnessModeMetadata(hbmMetadata);
        mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
                mDisplayDeviceConfig.getHighBrightnessModeData(),
                new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -1961,7 +1965,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
                    if (mAutomaticBrightnessController != null) {
                        mAutomaticBrightnessController.update();
                    }
                }, mContext);
                }, mHighBrightnessModeMetadata, mContext);
    }

    private BrightnessThrottler createBrightnessThrottlerLocked() {
+10 −5
Original line number Diff line number Diff line
@@ -328,6 +328,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
    private float[] mNitsRange;

    private final HighBrightnessModeController mHbmController;
    private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;

    private final BrightnessThrottler mBrightnessThrottler;

@@ -432,7 +433,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
            DisplayPowerCallbacks callbacks, Handler handler,
            SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
            BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
            Runnable onBrightnessChangeRunnable) {
            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {

        mInjector = injector != null ? injector : new Injector();
        mClock = mInjector.getClock();
@@ -448,6 +449,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
        mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
                mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
                () -> updatePowerState(), mDisplayId, mSensorManager);
        mHighBrightnessModeMetadata = hbmMetadata;
        mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController);
        mTag = "DisplayPowerController2[" + mDisplayId + "]";

@@ -707,7 +709,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
     * Make sure DisplayManagerService.mSyncRoot lock is held when this is called
     */
    @Override
    public void onDisplayChanged() {
    public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata) {
        final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
        if (device == null) {
            Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: "
@@ -721,6 +723,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
        final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
        final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
        final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();

        mHandler.post(() -> {
            boolean changed = false;
            if (mDisplayDevice != device) {
@@ -729,7 +732,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
                mUniqueDisplayId = uniqueId;
                mDisplayStatsId = mUniqueDisplayId.hashCode();
                mDisplayDeviceConfig = config;
                loadFromDisplayDeviceConfig(token, info);
                loadFromDisplayDeviceConfig(token, info, hbmMetadata);
                mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);

                // Since the underlying display-device changed, we really don't know the
@@ -778,7 +781,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
        }
    }

    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
                                             HighBrightnessModeMetadata hbmMetadata) {
        // All properties that depend on the associated DisplayDevice and the DDC must be
        // updated here.
        loadBrightnessRampRates();
@@ -790,6 +794,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
                    mBrightnessRampIncreaseMaxTimeMillis,
                    mBrightnessRampDecreaseMaxTimeMillis);
        }
        mHbmController.setHighBrightnessModeMetadata(hbmMetadata);
        mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
                mDisplayDeviceConfig.getHighBrightnessModeData(),
                new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -1740,7 +1745,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
                    if (mAutomaticBrightnessController != null) {
                        mAutomaticBrightnessController.update();
                    }
                }, mContext);
                }, mHighBrightnessModeMetadata, mContext);
    }

    private BrightnessThrottler createBrightnessThrottlerLocked() {
+6 −2
Original line number Diff line number Diff line
@@ -31,10 +31,14 @@ import java.io.PrintWriter;
public interface DisplayPowerControllerInterface {

    /**
     * Notified when the display is changed. We use this to apply any changes that might be needed
     * Notified when the display is changed.
     * We use this to apply any changes that might be needed
     * when displays get swapped on foldable devices.
     * We also pass the High brightness mode metadata like
     * remaining time and hbm events for the corresponding
     * physical display, to update the values correctly.
     */
    void onDisplayChanged();
    void onDisplayChanged(HighBrightnessModeMetadata hbmInfo);

    /**
     * Unregisters all listeners and interrupts all running threads; halting future work.
+46 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.display;


/**
 * Represents an event in which High Brightness Mode was enabled.
 */
class HbmEvent {
    private long mStartTimeMillis;
    private long mEndTimeMillis;

    HbmEvent(long startTimeMillis, long endTimeMillis) {
        this.mStartTimeMillis = startTimeMillis;
        this.mEndTimeMillis = endTimeMillis;
    }

    public long getStartTimeMillis() {
        return mStartTimeMillis;
    }

    public long getEndTimeMillis() {
        return mEndTimeMillis;
    }

    @Override
    public String toString() {
        return "HbmEvent: {startTimeMillis:" + mStartTimeMillis + ", endTimeMillis: "
                + mEndTimeMillis + "}, total: "
                + ((mEndTimeMillis - mStartTimeMillis) / 1000) + "]";
    }
}
Loading