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

Commit a49b90be authored by Daniel Solomon's avatar Daniel Solomon
Browse files

Change DisplayModeDirector's brightness source to DisplayListener

Currently, DisplayModeDirector listens to brightness changes from Settings.
However, brightness value in Settings is overridden for various reasons
inside DisplayPowerController before being propagated to HALs. As a
result, the brightness value that DisplayModeDirector is acting on isn't
always the brightness value that gets applied to the display.

This change,
- Changes DisplayPowerController so that the final brightness value (after all
modifications) is saved.
- Changes DisplayModeDirector to use DisplayManager.DisplayListener to
observe brightness changes and then retrieve and act on the final
brightness value saved in DisplayPowerController.

Bug: 199955532
Test: atest DisplayModeDirectorTest
Change-Id: Ia6dd7d194b6913d2942bea9c29a2011b8058beef
parent de460d15
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -60,6 +60,9 @@ public final class BrightnessInfo implements Parcelable {
    /** Brightness */
    public final float brightness;

    /** Brightness after {@link DisplayPowerController} adjustments */
    public final float adjustedBrightness;

    /** Current minimum supported brightness. */
    public final float brightnessMinimum;

@@ -74,7 +77,13 @@ public final class BrightnessInfo implements Parcelable {

    public BrightnessInfo(float brightness, float brightnessMinimum, float brightnessMaximum,
            @HighBrightnessMode int highBrightnessMode) {
        this(brightness, brightness, brightnessMinimum, brightnessMaximum, highBrightnessMode);
    }

    public BrightnessInfo(float brightness, float adjustedBrightness, float brightnessMinimum,
            float brightnessMaximum, @HighBrightnessMode int highBrightnessMode) {
        this.brightness = brightness;
        this.adjustedBrightness = adjustedBrightness;
        this.brightnessMinimum = brightnessMinimum;
        this.brightnessMaximum = brightnessMaximum;
        this.highBrightnessMode = highBrightnessMode;
@@ -103,6 +112,7 @@ public final class BrightnessInfo implements Parcelable {
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeFloat(brightness);
        dest.writeFloat(adjustedBrightness);
        dest.writeFloat(brightnessMinimum);
        dest.writeFloat(brightnessMaximum);
        dest.writeInt(highBrightnessMode);
@@ -123,6 +133,7 @@ public final class BrightnessInfo implements Parcelable {

    private BrightnessInfo(Parcel source) {
        brightness = source.readFloat();
        adjustedBrightness = source.readFloat();
        brightnessMinimum = source.readFloat();
        brightnessMaximum = source.readFloat();
        highBrightnessMode = source.readInt();
+40 −51
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.display;

import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
import static android.os.PowerManager.BRIGHTNESS_INVALID;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -40,6 +41,7 @@ import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -60,6 +62,7 @@ import android.view.DisplayInfo;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.display.utils.AmbientFilter;
@@ -155,7 +158,7 @@ public class DisplayModeDirector {
        mAppRequestObserver = new AppRequestObserver();
        mSettingsObserver = new SettingsObserver(context, handler);
        mDisplayObserver = new DisplayObserver(context, handler);
        mBrightnessObserver = new BrightnessObserver(context, handler);
        mBrightnessObserver = new BrightnessObserver(context, handler, injector);
        mUdfpsObserver = new UdfpsObserver();
        final BallotBox ballotBox = (displayId, priority, vote) -> {
            synchronized (mLock) {
@@ -1427,8 +1430,6 @@ public class DisplayModeDirector {
        @Override
        public void onDisplayChanged(int displayId) {
            updateDisplayModes(displayId);
            // TODO: Break the coupling between DisplayObserver and BrightnessObserver.
            mBrightnessObserver.onDisplayChanged(displayId);
        }

        private void updateDisplayModes(int displayId) {
@@ -1465,7 +1466,7 @@ public class DisplayModeDirector {
     * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
     */
    @VisibleForTesting
    public class BrightnessObserver extends ContentObserver {
    public class BrightnessObserver implements DisplayManager.DisplayListener {
        private final static int LIGHT_SENSOR_RATE_MS = 250;
        private int[] mLowDisplayBrightnessThresholds;
        private int[] mLowAmbientBrightnessThresholds;
@@ -1488,6 +1489,8 @@ public class DisplayModeDirector {
        private int mBrightness = -1;

        private final Context mContext;
        private final Injector mInjector;
        private final Handler mHandler;

        // Enable light sensor only when mShouldObserveAmbientLowChange is true or
        // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
@@ -1500,9 +1503,11 @@ public class DisplayModeDirector {
        private int mRefreshRateInLowZone;
        private int mRefreshRateInHighZone;

        BrightnessObserver(Context context, Handler handler) {
            super(handler);
        BrightnessObserver(Context context, Handler handler, Injector injector) {
            mContext = context;
            mHandler = handler;
            mInjector = injector;

            mLowDisplayBrightnessThresholds = context.getResources().getIntArray(
                    R.array.config_brightnessThresholdsOfPeakRefreshRate);
            mLowAmbientBrightnessThresholds = context.getResources().getIntArray(
@@ -1569,8 +1574,7 @@ public class DisplayModeDirector {
        public void observe(SensorManager sensorManager) {
            mSensorManager = sensorManager;
            final ContentResolver cr = mContext.getContentResolver();
            mBrightness = Settings.System.getIntForUser(cr,
                    Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
            mBrightness = getBrightness(Display.DEFAULT_DISPLAY);

            // DeviceConfig is accessible after system ready.
            int[] lowDisplayBrightnessThresholds =
@@ -1603,6 +1607,10 @@ public class DisplayModeDirector {

            restartObserver();
            mDeviceConfigDisplaySettings.startListening();

            mInjector.registerDisplayListener(this, mHandler,
                    DisplayManager.EVENT_FLAG_DISPLAY_CHANGED |
                    DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
        }

        public void setLoggingEnabled(boolean loggingEnabled) {
@@ -1718,28 +1726,30 @@ public class DisplayModeDirector {
            }
        }

        @Override
        public void onDisplayAdded(int displayId) {}

        @Override
        public void onDisplayRemoved(int displayId) {}

        @Override
        public void onDisplayChanged(int displayId) {
            if (displayId == Display.DEFAULT_DISPLAY) {
                updateDefaultDisplayState();
            }
        }

        @Override
        public void onChange(boolean selfChange, Uri uri, int userId) {
                // We don't support multiple display blocking zones yet, so only handle
                // brightness changes for the default display for now.
                int brightness = getBrightness(displayId);
                synchronized (mLock) {
                final ContentResolver cr = mContext.getContentResolver();
                int brightness = Settings.System.getIntForUser(cr,
                        Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
                    if (brightness != mBrightness) {
                        mBrightness = brightness;
                        onBrightnessChangedLocked();
                    }
                }
            }
        }

        private void restartObserver() {
            final ContentResolver cr = mContext.getContentResolver();

            if (mRefreshRateInLowZone > 0) {
                mShouldObserveDisplayLowChange = hasValidThreshold(
                        mLowDisplayBrightnessThresholds);
@@ -1760,15 +1770,6 @@ public class DisplayModeDirector {
                mShouldObserveAmbientHighChange = false;
            }

            if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) {
                // Content Service does not check if an listener has already been registered.
                // To ensure only one listener is registered, force an unregistration first.
                mInjector.unregisterBrightnessObserver(cr, this);
                mInjector.registerBrightnessObserver(cr, this);
            } else {
                mInjector.unregisterBrightnessObserver(cr, this);
            }

            if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
                Resources resources = mContext.getResources();
                String lightSensorType = resources.getString(
@@ -1968,6 +1969,15 @@ public class DisplayModeDirector {
            return mDefaultDisplayState == Display.STATE_ON;
        }

        private int getBrightness(int displayId) {
            final BrightnessInfo info = mInjector.getBrightnessInfo(displayId);
            if (info != null) {
                return BrightnessSynchronizer.brightnessFloatToInt(info.adjustedBrightness);
            }

            return BRIGHTNESS_INVALID;
        }

        private final class LightSensorEventListener implements SensorEventListener {
            final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
            private float mLastSensorData;
@@ -2630,19 +2640,11 @@ public class DisplayModeDirector {
    }

    interface Injector {
        // TODO: brightnessfloat: change this to the float setting
        Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
        Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);

        @NonNull
        DeviceConfigInterface getDeviceConfig();

        void registerBrightnessObserver(@NonNull ContentResolver cr,
                @NonNull ContentObserver observer);

        void unregisterBrightnessObserver(@NonNull ContentResolver cr,
                @NonNull ContentObserver observer);

        void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
                @NonNull ContentObserver observer);

@@ -2671,19 +2673,6 @@ public class DisplayModeDirector {
            return DeviceConfigInterface.REAL;
        }

        @Override
        public void registerBrightnessObserver(@NonNull ContentResolver cr,
                @NonNull ContentObserver observer) {
            cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/,
                    observer, UserHandle.USER_SYSTEM);
        }

        @Override
        public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
                @NonNull ContentObserver observer) {
            cr.unregisterContentObserver(observer);
        }

        @Override
        public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
                @NonNull ContentObserver observer) {
+22 −4
Original line number Diff line number Diff line
@@ -1259,10 +1259,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
            putScreenBrightnessSetting(brightnessState, /* updateCurrent */ true);
        }

        // We save the brightness info *after* the brightness setting has been changed so that
        // the brightness info reflects the latest value.
        saveBrightnessInfo(getScreenBrightnessSetting());

        // Apply dimming by at least some minimum amount when user activity
        // timeout is about to expire.
        if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
@@ -1393,6 +1389,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
                        hadUserBrightnessPoint);
            }

            // We save the brightness info *after* the brightness setting has been changed and
            // adjustments made so that the brightness info reflects the latest value.
            saveBrightnessInfo(getScreenBrightnessSetting(), animateValue);
        } else {
            saveBrightnessInfo(getScreenBrightnessSetting());
        }

        // Log any changes to what is currently driving the brightness setting.
@@ -1509,6 +1510,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
        synchronized (mCachedBrightnessInfo) {
            return new BrightnessInfo(
                    mCachedBrightnessInfo.brightness,
                    mCachedBrightnessInfo.adjustedBrightness,
                    mCachedBrightnessInfo.brightnessMin,
                    mCachedBrightnessInfo.brightnessMax,
                    mCachedBrightnessInfo.hbmMode);
@@ -1516,8 +1518,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
    }

    private void saveBrightnessInfo(float brightness) {
        saveBrightnessInfo(brightness, brightness);
    }

    private void saveBrightnessInfo(float brightness, float adjustedBrightness) {
        synchronized (mCachedBrightnessInfo) {
            mCachedBrightnessInfo.brightness = brightness;
            mCachedBrightnessInfo.adjustedBrightness = adjustedBrightness;
            mCachedBrightnessInfo.brightnessMin = mHbmController.getCurrentBrightnessMin();
            mCachedBrightnessInfo.brightnessMax = mHbmController.getCurrentBrightnessMax();
            mCachedBrightnessInfo.hbmMode = mHbmController.getHighBrightnessMode();
@@ -2195,6 +2202,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
        pw.println("  mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
        pw.println("  mColorFadeFadesConfig=" + mColorFadeFadesConfig);
        pw.println("  mColorFadeEnabled=" + mColorFadeEnabled);
        synchronized (mCachedBrightnessInfo) {
            pw.println("  mCachedBrightnessInfo.brightness=" + mCachedBrightnessInfo.brightness);
            pw.println("  mCachedBrightnessInfo.adjustedBrightness=" +
                    mCachedBrightnessInfo.adjustedBrightness);
            pw.println("  mCachedBrightnessInfo.brightnessMin=" +
                    mCachedBrightnessInfo.brightnessMin);
            pw.println("  mCachedBrightnessInfo.brightnessMax=" +
                    mCachedBrightnessInfo.brightnessMax);
            pw.println("  mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode);
        }
        pw.println("  mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
        pw.println("  mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);

@@ -2606,6 +2623,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call

    static class CachedBrightnessInfo {
        public float brightness;
        public float adjustedBrightness;
        public float brightnessMin;
        public float brightnessMax;
        public int hbmMode;
+41 −38
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.Preconditions;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
@@ -751,19 +752,27 @@ public class DisplayModeDirectorTest {

        director.start(sensorManager);

        ArgumentCaptor<SensorEventListener> listenerCaptor =
        ArgumentCaptor<DisplayListener> displayListenerCaptor =
                  ArgumentCaptor.forClass(DisplayListener.class);
        verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
                any(Handler.class),
                eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
                    | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
        DisplayListener displayListener = displayListenerCaptor.getValue();

        ArgumentCaptor<SensorEventListener> sensorListenerCaptor =
                ArgumentCaptor.forClass(SensorEventListener.class);
        Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
                .registerListener(
                        listenerCaptor.capture(),
                        sensorListenerCaptor.capture(),
                        eq(lightSensor),
                        anyInt(),
                        any(Handler.class));
        SensorEventListener listener = listenerCaptor.getValue();
        SensorEventListener sensorListener = sensorListenerCaptor.getValue();

        setBrightness(10);
        setBrightness(10, 10, displayListener);
        // Sensor reads 20 lux,
        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
        sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));

        Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
        assertVoteForRefreshRate(vote, 90 /*fps*/);
@@ -771,9 +780,11 @@ public class DisplayModeDirectorTest {
        assertThat(vote).isNotNull();
        assertThat(vote.disableRefreshRateSwitching).isTrue();

        setBrightness(125);
        // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
        // parameter to the necessary threshold
        setBrightness(10, 125, displayListener);
        // Sensor reads 1000 lux,
        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));
        sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));

        vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
        assertThat(vote).isNull();
@@ -799,6 +810,14 @@ public class DisplayModeDirectorTest {

        director.start(sensorManager);

        ArgumentCaptor<DisplayListener> displayListenerCaptor =
                  ArgumentCaptor.forClass(DisplayListener.class);
        verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
                any(Handler.class),
                eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
                    | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
        DisplayListener displayListener = displayListenerCaptor.getValue();

        ArgumentCaptor<SensorEventListener> listenerCaptor =
                ArgumentCaptor.forClass(SensorEventListener.class);
        verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
@@ -807,20 +826,22 @@ public class DisplayModeDirectorTest {
                        eq(lightSensor),
                        anyInt(),
                        any(Handler.class));
        SensorEventListener listener = listenerCaptor.getValue();
        SensorEventListener sensorListener = listenerCaptor.getValue();

        setBrightness(100);
        setBrightness(100, 100, displayListener);
        // Sensor reads 2000 lux,
        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
        sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));

        Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
        assertThat(vote).isNull();
        vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
        assertThat(vote).isNull();

        setBrightness(255);
        // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
        // parameter to the necessary threshold
        setBrightness(100, 255, displayListener);
        // Sensor reads 9000 lux,
        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
        sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));

        vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
        assertVoteForRefreshRate(vote, 60 /*fps*/);
@@ -1834,11 +1855,14 @@ public class DisplayModeDirectorTest {
        }
    }

    private void setBrightness(int brightness) {
        Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS,
                brightness);
        mInjector.notifyBrightnessChanged();
        waitForIdleSync();
    private void setBrightness(int brightness, int adjustedBrightness, DisplayListener listener) {
        float floatBri = BrightnessSynchronizer.brightnessIntToFloat(brightness);
        float floatAdjBri = BrightnessSynchronizer.brightnessIntToFloat(adjustedBrightness);

        when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                new BrightnessInfo(floatBri, floatAdjBri, 0.0f, 1.0f,
                    BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
        listener.onDisplayChanged(DISPLAY_ID);
    }

    private void setPeakRefreshRate(float fps) {
@@ -1901,27 +1925,6 @@ public class DisplayModeDirectorTest {
            return mDeviceConfig;
        }

        @Override
        public void registerBrightnessObserver(@NonNull ContentResolver cr,
                @NonNull ContentObserver observer) {
            if (mBrightnessObserver != null) {
                throw new IllegalStateException("Tried to register a second brightness observer");
            }
            mBrightnessObserver = observer;
        }

        @Override
        public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
                @NonNull ContentObserver observer) {
            mBrightnessObserver = null;
        }

        void notifyBrightnessChanged() {
            if (mBrightnessObserver != null) {
                mBrightnessObserver.dispatchChange(false /*selfChange*/, DISPLAY_BRIGHTNESS_URI);
            }
        }

        @Override
        public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
                @NonNull ContentObserver observer) {