diff options
6 files changed, 344 insertions, 234 deletions
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java index cf44ac029c82..a1fd16476706 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java @@ -74,6 +74,5 @@ abstract class BrightnessClamper<T> { protected enum Type { POWER, - WEAR_BEDTIME_MODE, } } diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java index 9404034cdd34..a10094fdfbb8 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java @@ -218,9 +218,7 @@ public class BrightnessClamperController { return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; } else if (mClamperType == Type.POWER) { return BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC; - } else if (mClamperType == Type.WEAR_BEDTIME_MODE) { - return BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE; - } else { + } else { Slog.wtf(TAG, "BrightnessMaxReason not mapped for type=" + mClamperType); return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; } @@ -350,10 +348,6 @@ public class BrightnessClamperController { data, currentBrightness)); } } - if (flags.isBrightnessWearBedtimeModeClamperEnabled()) { - clampers.add(new BrightnessWearBedtimeModeClamper(handler, context, - clamperChangeListener, data)); - } return clampers; } @@ -362,6 +356,10 @@ public class BrightnessClamperController { DisplayDeviceData data) { List<BrightnessStateModifier> modifiers = new ArrayList<>(); modifiers.add(new BrightnessThermalModifier(handler, listener, data)); + if (flags.isBrightnessWearBedtimeModeClamperEnabled()) { + modifiers.add(new BrightnessWearBedtimeModeModifier(handler, context, + listener, data)); + } modifiers.add(new DisplayDimModifier(context)); modifiers.add(new BrightnessLowPowerModeModifier()); @@ -395,7 +393,7 @@ public class BrightnessClamperController { */ public static class DisplayDeviceData implements BrightnessThermalModifier.ThermalData, BrightnessPowerClamper.PowerData, - BrightnessWearBedtimeModeClamper.WearBedtimeModeData { + BrightnessWearBedtimeModeModifier.WearBedtimeModeData { @NonNull private final String mUniqueDisplayId; @NonNull diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java deleted file mode 100644 index 1902e35ed397..000000000000 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.brightness.clamper; - -import android.annotation.NonNull; -import android.content.ContentResolver; -import android.content.Context; -import android.database.ContentObserver; -import android.os.Handler; -import android.os.UserHandle; -import android.provider.Settings; - -import com.android.internal.annotations.VisibleForTesting; - -public class BrightnessWearBedtimeModeClamper extends - BrightnessClamper<BrightnessWearBedtimeModeClamper.WearBedtimeModeData> { - - public static final int BEDTIME_MODE_OFF = 0; - public static final int BEDTIME_MODE_ON = 1; - - private final Context mContext; - - private final ContentObserver mSettingsObserver; - - BrightnessWearBedtimeModeClamper(Handler handler, Context context, - BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) { - this(new Injector(), handler, context, listener, data); - } - - @VisibleForTesting - BrightnessWearBedtimeModeClamper(Injector injector, Handler handler, Context context, - BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) { - super(handler, listener); - mContext = context; - mBrightnessCap = data.getBrightnessWearBedtimeModeCap(); - mSettingsObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange) { - final int bedtimeModeSetting = Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.Wearable.BEDTIME_MODE, - BEDTIME_MODE_OFF); - mIsActive = bedtimeModeSetting == BEDTIME_MODE_ON; - mChangeListener.onChanged(); - } - }; - injector.registerBedtimeModeObserver(context.getContentResolver(), mSettingsObserver); - } - - @NonNull - @Override - Type getType() { - return Type.WEAR_BEDTIME_MODE; - } - - @Override - void onDeviceConfigChanged() {} - - @Override - void onDisplayChanged(WearBedtimeModeData displayData) { - mHandler.post(() -> { - mBrightnessCap = displayData.getBrightnessWearBedtimeModeCap(); - mChangeListener.onChanged(); - }); - } - - @Override - void stop() { - mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); - } - - interface WearBedtimeModeData { - float getBrightnessWearBedtimeModeCap(); - } - - @VisibleForTesting - static class Injector { - void registerBedtimeModeObserver(@NonNull ContentResolver cr, - @NonNull ContentObserver observer) { - cr.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE), - /* notifyForDescendants= */ false, observer, UserHandle.USER_ALL); - } - } -} diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeModifier.java new file mode 100644 index 000000000000..c9c8c33764a6 --- /dev/null +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeModifier.java @@ -0,0 +1,158 @@ +/* + * 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.brightness.clamper; + +import android.annotation.NonNull; +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.hardware.display.BrightnessInfo; +import android.hardware.display.DisplayManagerInternal; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.display.DisplayBrightnessState; +import com.android.server.display.brightness.BrightnessReason; + +import java.io.PrintWriter; + +public class BrightnessWearBedtimeModeModifier implements BrightnessStateModifier, + BrightnessClamperController.DisplayDeviceDataListener, + BrightnessClamperController.StatefulModifier { + + public static final int BEDTIME_MODE_OFF = 0; + public static final int BEDTIME_MODE_ON = 1; + + private final Context mContext; + + private final ContentObserver mSettingsObserver; + protected final Handler mHandler; + protected final BrightnessClamperController.ClamperChangeListener mChangeListener; + + private float mBrightnessCap; + private boolean mIsActive = false; + private boolean mApplied = false; + + BrightnessWearBedtimeModeModifier(Handler handler, Context context, + BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) { + this(new Injector(), handler, context, listener, data); + } + + @VisibleForTesting + BrightnessWearBedtimeModeModifier(Injector injector, Handler handler, Context context, + BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) { + mHandler = handler; + mChangeListener = listener; + mContext = context; + mBrightnessCap = data.getBrightnessWearBedtimeModeCap(); + mSettingsObserver = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + final int bedtimeModeSetting = Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.Wearable.BEDTIME_MODE, + BEDTIME_MODE_OFF); + mIsActive = bedtimeModeSetting == BEDTIME_MODE_ON; + mChangeListener.onChanged(); + } + }; + injector.registerBedtimeModeObserver(context.getContentResolver(), mSettingsObserver); + } + + //region BrightnessStateModifier + @Override + public void apply(DisplayManagerInternal.DisplayPowerRequest request, + DisplayBrightnessState.Builder stateBuilder) { + if (mIsActive && stateBuilder.getMaxBrightness() > mBrightnessCap) { + stateBuilder.setMaxBrightness(mBrightnessCap); + stateBuilder.setBrightness(Math.min(stateBuilder.getBrightness(), mBrightnessCap)); + stateBuilder.setBrightnessMaxReason( + BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE); + stateBuilder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_THROTTLED); + // set fast change only when modifier is activated. + // this will allow auto brightness to apply slow change even when modifier is active + if (!mApplied) { + stateBuilder.setIsSlowChange(false); + } + mApplied = true; + } else { + mApplied = false; + } + } + + @Override + public void stop() { + mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); + } + + @Override + public void dump(PrintWriter writer) { + writer.println("BrightnessWearBedtimeModeModifier:"); + writer.println(" mBrightnessCap: " + mBrightnessCap); + writer.println(" mIsActive: " + mIsActive); + writer.println(" mApplied: " + mApplied); + } + + @Override + public boolean shouldListenToLightSensor() { + return false; + } + + @Override + public void setAmbientLux(float lux) { + // noop + } + //endregion + + //region DisplayDeviceDataListener + @Override + public void onDisplayChanged(BrightnessClamperController.DisplayDeviceData data) { + mHandler.post(() -> { + mBrightnessCap = data.getBrightnessWearBedtimeModeCap(); + mChangeListener.onChanged(); + }); + } + //endregion + + //region StatefulModifier + @Override + public void applyStateChange( + BrightnessClamperController.ModifiersAggregatedState aggregatedState) { + if (mIsActive && aggregatedState.mMaxBrightness > mBrightnessCap) { + aggregatedState.mMaxBrightness = mBrightnessCap; + aggregatedState.mMaxBrightnessReason = + BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE; + } + } + //endregion + + interface WearBedtimeModeData { + float getBrightnessWearBedtimeModeCap(); + } + + @VisibleForTesting + static class Injector { + void registerBedtimeModeObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + cr.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE), + /* notifyForDescendants= */ false, observer, UserHandle.USER_ALL); + } + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java deleted file mode 100644 index 306b4f86024e..000000000000 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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.brightness.clamper; - -import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeClamper.BEDTIME_MODE_OFF; -import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeClamper.BEDTIME_MODE_ON; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.verify; - -import android.content.ContentResolver; -import android.database.ContentObserver; -import android.provider.Settings; -import android.testing.TestableContext; - -import androidx.annotation.NonNull; -import androidx.test.platform.app.InstrumentationRegistry; - -import com.android.internal.display.BrightnessSynchronizer; -import com.android.server.testutils.TestHandler; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -public class BrightnessWearBedtimeModeClamperTest { - - private static final float BRIGHTNESS_CAP = 0.3f; - - @Mock - private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener; - - @Rule - public final TestableContext mContext = new TestableContext( - InstrumentationRegistry.getInstrumentation().getContext()); - - private final TestHandler mTestHandler = new TestHandler(null); - private final TestInjector mInjector = new TestInjector(); - private BrightnessWearBedtimeModeClamper mClamper; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mClamper = new BrightnessWearBedtimeModeClamper(mInjector, mTestHandler, mContext, - mMockClamperChangeListener, () -> BRIGHTNESS_CAP); - mTestHandler.flush(); - } - - @Test - public void testBrightnessCap() { - assertEquals(BRIGHTNESS_CAP, mClamper.getBrightnessCap(), BrightnessSynchronizer.EPSILON); - } - - @Test - public void testBedtimeModeOn() { - setBedtimeModeEnabled(true); - assertTrue(mClamper.isActive()); - verify(mMockClamperChangeListener).onChanged(); - } - - @Test - public void testBedtimeModeOff() { - setBedtimeModeEnabled(false); - assertFalse(mClamper.isActive()); - verify(mMockClamperChangeListener).onChanged(); - } - - @Test - public void testType() { - assertEquals(BrightnessClamper.Type.WEAR_BEDTIME_MODE, mClamper.getType()); - } - - @Test - public void testOnDisplayChanged() { - float newBrightnessCap = 0.61f; - - mClamper.onDisplayChanged(() -> newBrightnessCap); - mTestHandler.flush(); - - assertEquals(newBrightnessCap, mClamper.getBrightnessCap(), BrightnessSynchronizer.EPSILON); - verify(mMockClamperChangeListener).onChanged(); - } - - private void setBedtimeModeEnabled(boolean enabled) { - Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.Wearable.BEDTIME_MODE, - enabled ? BEDTIME_MODE_ON : BEDTIME_MODE_OFF); - mInjector.notifyBedtimeModeChanged(); - mTestHandler.flush(); - } - - private static class TestInjector extends BrightnessWearBedtimeModeClamper.Injector { - - private ContentObserver mObserver; - - @Override - void registerBedtimeModeObserver(@NonNull ContentResolver cr, - @NonNull ContentObserver observer) { - mObserver = observer; - } - - private void notifyBedtimeModeChanged() { - if (mObserver != null) { - mObserver.dispatchChange(/* selfChange= */ false, - Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE)); - } - } - } -} diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeModifierTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeModifierTest.java new file mode 100644 index 000000000000..8271a0f44fc6 --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeModifierTest.java @@ -0,0 +1,180 @@ +/* + * 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.brightness.clamper; + +import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeModifier.BEDTIME_MODE_OFF; +import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeModifier.BEDTIME_MODE_ON; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.database.ContentObserver; +import android.hardware.display.BrightnessInfo; +import android.hardware.display.DisplayManagerInternal; +import android.os.IBinder; +import android.os.PowerManager; +import android.provider.Settings; +import android.testing.TestableContext; + +import androidx.annotation.NonNull; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.internal.display.BrightnessSynchronizer; +import com.android.server.display.DisplayBrightnessState; +import com.android.server.display.DisplayDeviceConfig; +import com.android.server.display.brightness.BrightnessReason; +import com.android.server.display.brightness.clamper.BrightnessClamperController.ModifiersAggregatedState; +import com.android.server.testutils.TestHandler; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class BrightnessWearBedtimeModeModifierTest { + private static final int NO_MODIFIER = 0; + private static final float BRIGHTNESS_CAP = 0.3f; + private static final String DISPLAY_ID = "displayId"; + + @Mock + private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener; + @Mock + private DisplayManagerInternal.DisplayPowerRequest mMockRequest; + @Mock + private DisplayDeviceConfig mMockDisplayDeviceConfig; + @Mock + private IBinder mMockBinder; + + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getContext()); + + private final TestHandler mTestHandler = new TestHandler(null); + private final TestInjector mInjector = new TestInjector(); + private BrightnessWearBedtimeModeModifier mModifier; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mModifier = new BrightnessWearBedtimeModeModifier(mInjector, mTestHandler, mContext, + mMockClamperChangeListener, () -> BRIGHTNESS_CAP); + mTestHandler.flush(); + } + + @Test + public void testBedtimeModeOff() { + setBedtimeModeEnabled(false); + assertModifierState( + 0.5f, true, + PowerManager.BRIGHTNESS_MAX, 0.5f, + false, true); + verify(mMockClamperChangeListener).onChanged(); + } + + @Test + public void testBedtimeModeOn() { + setBedtimeModeEnabled(true); + assertModifierState( + 0.5f, true, + BRIGHTNESS_CAP, BRIGHTNESS_CAP, + true, false); + verify(mMockClamperChangeListener).onChanged(); + } + + @Test + public void testOnDisplayChanged() { + setBedtimeModeEnabled(true); + clearInvocations(mMockClamperChangeListener); + float newBrightnessCap = 0.61f; + onDisplayChange(newBrightnessCap); + mTestHandler.flush(); + + assertModifierState( + 0.5f, true, + newBrightnessCap, 0.5f, + true, false); + verify(mMockClamperChangeListener).onChanged(); + } + + private void setBedtimeModeEnabled(boolean enabled) { + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.Wearable.BEDTIME_MODE, + enabled ? BEDTIME_MODE_ON : BEDTIME_MODE_OFF); + mInjector.notifyBedtimeModeChanged(); + mTestHandler.flush(); + } + + private void onDisplayChange(float brightnessCap) { + when(mMockDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode()) + .thenReturn(brightnessCap); + mModifier.onDisplayChanged(ClamperTestUtilsKt.createDisplayDeviceData( + mMockDisplayDeviceConfig, mMockBinder, DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID)); + } + + private void assertModifierState( + float currentBrightness, + boolean currentSlowChange, + float maxBrightness, float brightness, + boolean isActive, + boolean isSlowChange) { + ModifiersAggregatedState modifierState = new ModifiersAggregatedState(); + DisplayBrightnessState.Builder stateBuilder = DisplayBrightnessState.builder(); + stateBuilder.setBrightness(currentBrightness); + stateBuilder.setIsSlowChange(currentSlowChange); + + int maxBrightnessReason = isActive ? BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE + : BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; + int modifier = isActive ? BrightnessReason.MODIFIER_THROTTLED : NO_MODIFIER; + + mModifier.applyStateChange(modifierState); + assertThat(modifierState.mMaxBrightness).isEqualTo(maxBrightness); + assertThat(modifierState.mMaxBrightnessReason).isEqualTo(maxBrightnessReason); + + mModifier.apply(mMockRequest, stateBuilder); + + assertThat(stateBuilder.getMaxBrightness()) + .isWithin(BrightnessSynchronizer.EPSILON).of(maxBrightness); + assertThat(stateBuilder.getBrightness()) + .isWithin(BrightnessSynchronizer.EPSILON).of(brightness); + assertThat(stateBuilder.getBrightnessMaxReason()).isEqualTo(maxBrightnessReason); + assertThat(stateBuilder.getBrightnessReason().getModifier()).isEqualTo(modifier); + assertThat(stateBuilder.isSlowChange()).isEqualTo(isSlowChange); + } + + + private static class TestInjector extends BrightnessWearBedtimeModeModifier.Injector { + + private ContentObserver mObserver; + + @Override + void registerBedtimeModeObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + mObserver = observer; + } + + private void notifyBedtimeModeChanged() { + if (mObserver != null) { + mObserver.dispatchChange(/* selfChange= */ false, + Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE)); + } + } + } +} |