diff options
7 files changed, 297 insertions, 75 deletions
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 25e704b8861b..0f89a6e04cf9 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -254,13 +254,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // The doze screen brightness. private final float mScreenBrightnessDozeConfig; - // The dim screen brightness. - private final float mScreenBrightnessDimConfig; - - // The minimum dim amount to use if the screen brightness is already below - // mScreenBrightnessDimConfig. - private final float mScreenBrightnessMinimumDimAmount; - // True if auto-brightness should be used. private boolean mUseSoftwareAutoBrightnessConfig; @@ -529,11 +522,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // DOZE AND DIM SETTINGS mScreenBrightnessDozeConfig = BrightnessUtils.clampAbsoluteBrightness( pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE)); - mScreenBrightnessDimConfig = BrightnessUtils.clampAbsoluteBrightness( - pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM)); - mScreenBrightnessMinimumDimAmount = resources.getFloat( - R.dimen.config_screenBrightnessMinimumDimAmountFloat); - loadBrightnessRampRates(); mSkipScreenOnBrightnessRamp = resources.getBoolean( R.bool.config_skipScreenOnBrightnessRamp); @@ -565,7 +553,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mUniqueDisplayId, mThermalBrightnessThrottlingDataId, mDisplayDeviceConfig - )); + ), mContext); // Seed the cached brightness saveBrightnessInfo(getScreenBrightnessSetting()); mAutomaticBrightnessStrategy = @@ -1426,6 +1414,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // Note throttling effectively changes the allowed brightness range, so, similarly to HBM, // we broadcast this change through setting. final float unthrottledBrightnessState = brightnessState; + if (mBrightnessThrottler.isThrottled()) { mTempBrightnessEvent.setThermalMax(mBrightnessThrottler.getBrightnessCap()); brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap()); @@ -1449,25 +1438,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessState); } - // Apply dimming by at least some minimum amount when user activity - // timeout is about to expire. - if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) { - if (brightnessState > PowerManager.BRIGHTNESS_MIN) { - brightnessState = Math.max( - Math.min(brightnessState - mScreenBrightnessMinimumDimAmount, - mScreenBrightnessDimConfig), - PowerManager.BRIGHTNESS_MIN); - mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED); - } - if (!mAppliedDimming) { - slowChange = false; - } - mAppliedDimming = true; - } else if (mAppliedDimming) { - slowChange = false; - mAppliedDimming = false; - } - DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest, brightnessState, slowChange); @@ -2366,7 +2336,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal pw.println(); pw.println("Display Power Controller Configuration:"); pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig); - pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig); pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig); pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp); pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig); 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 f19d00b82aab..9b289898de7e 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 @@ -20,6 +20,7 @@ import static com.android.server.display.brightness.clamper.BrightnessClamper.Ty import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; import android.hardware.display.DisplayManagerInternal; import android.os.Handler; import android.os.HandlerExecutor; @@ -61,13 +62,13 @@ public class BrightnessClamperController { private Type mClamperType = null; public BrightnessClamperController(Handler handler, - ClamperChangeListener clamperChangeListener, DisplayDeviceData data) { - this(new Injector(), handler, clamperChangeListener, data); + ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context) { + this(new Injector(), handler, clamperChangeListener, data, context); } @VisibleForTesting BrightnessClamperController(Injector injector, Handler handler, - ClamperChangeListener clamperChangeListener, DisplayDeviceData data) { + ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context) { mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider(); mHandler = handler; mClamperChangeListenerExternal = clamperChangeListener; @@ -85,6 +86,7 @@ public class BrightnessClamperController { mClampers.add( new BrightnessThermalClamper(handler, clamperChangeListenerInternal, data)); } + mModifiers.add(new DisplayDimModifier(context)); mModifiers.add(new BrightnessLowPowerModeModifier()); start(); } @@ -111,6 +113,7 @@ public class BrightnessClamperController { for (int i = 0; i < mModifiers.size(); i++) { mModifiers.get(i).apply(request, builder); } + return builder.build(); } diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java index f48ad2f19d6b..b478952ee5bd 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java @@ -18,44 +18,37 @@ package com.android.server.display.brightness.clamper; import android.hardware.display.DisplayManagerInternal; import android.os.PowerManager; +import android.util.IndentingPrintWriter; -import com.android.server.display.DisplayBrightnessState; import com.android.server.display.brightness.BrightnessReason; import java.io.PrintWriter; -class BrightnessLowPowerModeModifier implements BrightnessModifier { +class BrightnessLowPowerModeModifier extends BrightnessModifier { - private boolean mAppliedLowPower = false; + @Override + boolean shouldApply(DisplayManagerInternal.DisplayPowerRequest request) { + return request.lowPowerMode; + } + + + @Override + float getBrightnessAdjusted(float currentBrightness, + DisplayManagerInternal.DisplayPowerRequest request) { + final float brightnessFactor = + Math.min(request.screenLowPowerBrightnessFactor, 1); + return Math.max((currentBrightness * brightnessFactor), PowerManager.BRIGHTNESS_MIN); + } @Override - public void apply(DisplayManagerInternal.DisplayPowerRequest request, - DisplayBrightnessState.Builder stateBuilder) { - // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor - // as long as it is above the minimum threshold. - if (request.lowPowerMode) { - float value = stateBuilder.getBrightness(); - if (value > PowerManager.BRIGHTNESS_MIN) { - final float brightnessFactor = - Math.min(request.screenLowPowerBrightnessFactor, 1); - final float lowPowerBrightnessFloat = Math.max((value * brightnessFactor), - PowerManager.BRIGHTNESS_MIN); - stateBuilder.setBrightness(lowPowerBrightnessFloat); - stateBuilder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_LOW_POWER); - } - if (!mAppliedLowPower) { - stateBuilder.setIsSlowChange(false); - } - mAppliedLowPower = true; - } else if (mAppliedLowPower) { - stateBuilder.setIsSlowChange(false); - mAppliedLowPower = false; - } + int getModifier() { + return BrightnessReason.MODIFIER_LOW_POWER; } @Override public void dump(PrintWriter pw) { pw.println("BrightnessLowPowerModeModifier:"); - pw.println(" mAppliedLowPower=" + mAppliedLowPower); + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + super.dump(ipw); } } diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java index 3a33df657ff8..112e63dc62d4 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java @@ -17,6 +17,7 @@ package com.android.server.display.brightness.clamper; import android.hardware.display.DisplayManagerInternal; +import android.os.PowerManager; import com.android.server.display.DisplayBrightnessState; @@ -25,10 +26,39 @@ import java.io.PrintWriter; /** * Modifies current brightness based on request */ -interface BrightnessModifier { +abstract class BrightnessModifier { + + private boolean mApplied = false; + + abstract boolean shouldApply(DisplayManagerInternal.DisplayPowerRequest request); + + abstract float getBrightnessAdjusted(float currentBrightness, + DisplayManagerInternal.DisplayPowerRequest request); + + abstract int getModifier(); void apply(DisplayManagerInternal.DisplayPowerRequest request, - DisplayBrightnessState.Builder builder); + DisplayBrightnessState.Builder stateBuilder) { + // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor + // as long as it is above the minimum threshold. + if (shouldApply(request)) { + float value = stateBuilder.getBrightness(); + if (value > PowerManager.BRIGHTNESS_MIN) { + stateBuilder.setBrightness(getBrightnessAdjusted(value, request)); + stateBuilder.getBrightnessReason().addModifier(getModifier()); + } + if (!mApplied) { + stateBuilder.setIsSlowChange(false); + } + mApplied = true; + } else if (mApplied) { + stateBuilder.setIsSlowChange(false); + mApplied = false; + } + } - void dump(PrintWriter pw); + void dump(PrintWriter pw) { + pw.println("BrightnessModifier:"); + pw.println(" mApplied=" + mApplied); + } } diff --git a/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java new file mode 100644 index 000000000000..4ff7bdb93d22 --- /dev/null +++ b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java @@ -0,0 +1,79 @@ +/* + * 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.content.Context; +import android.content.res.Resources; +import android.hardware.display.DisplayManagerInternal; +import android.os.PowerManager; +import android.util.IndentingPrintWriter; + +import com.android.internal.R; +import com.android.server.display.brightness.BrightnessReason; +import com.android.server.display.brightness.BrightnessUtils; + +import java.io.PrintWriter; +import java.util.Objects; + +class DisplayDimModifier extends BrightnessModifier { + + // The dim screen brightness. + private final float mScreenBrightnessDimConfig; + + // The minimum dim amount to use if the screen brightness is already below + // mScreenBrightnessDimConfig. + private final float mScreenBrightnessMinimumDimAmount; + + DisplayDimModifier(Context context) { + PowerManager pm = Objects.requireNonNull(context.getSystemService(PowerManager.class)); + Resources resources = context.getResources(); + + mScreenBrightnessDimConfig = BrightnessUtils.clampAbsoluteBrightness( + pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM)); + mScreenBrightnessMinimumDimAmount = resources.getFloat( + R.dimen.config_screenBrightnessMinimumDimAmountFloat); + } + + + @Override + boolean shouldApply(DisplayManagerInternal.DisplayPowerRequest request) { + return request.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM; + } + + @Override + float getBrightnessAdjusted(float currentBrightness, + DisplayManagerInternal.DisplayPowerRequest request) { + return Math.max( + Math.min(currentBrightness - mScreenBrightnessMinimumDimAmount, + mScreenBrightnessDimConfig), + PowerManager.BRIGHTNESS_MIN); + } + + @Override + int getModifier() { + return BrightnessReason.MODIFIER_DIMMED; + } + + @Override + public void dump(PrintWriter pw) { + pw.println("DisplayDimModifier:"); + pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig); + pw.println(" mScreenBrightnessMinimumDimAmount=" + mScreenBrightnessMinimumDimAmount); + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + super.dump(ipw); + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifierTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifierTest.java index 266f5c198f03..0ff47247dfb0 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifierTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifierTest.java @@ -39,11 +39,11 @@ public class BrightnessLowPowerModeModifierTest { DEFAULT_BRIGHTNESS * LOW_POWER_BRIGHTNESS_FACTOR; private final DisplayPowerRequest mRequest = new DisplayPowerRequest(); private final DisplayBrightnessState.Builder mBuilder = prepareBuilder(); - private BrightnessLowPowerModeModifier mClamper; + private BrightnessLowPowerModeModifier mModifier; @Before public void setUp() { - mClamper = new BrightnessLowPowerModeModifier(); + mModifier = new BrightnessLowPowerModeModifier(); mRequest.screenLowPowerBrightnessFactor = LOW_POWER_BRIGHTNESS_FACTOR; mRequest.lowPowerMode = true; } @@ -52,7 +52,7 @@ public class BrightnessLowPowerModeModifierTest { public void testApply_lowPowerModeOff() { mRequest.lowPowerMode = false; - mClamper.apply(mRequest, mBuilder); + mModifier.apply(mRequest, mBuilder); assertEquals(DEFAULT_BRIGHTNESS, mBuilder.getBrightness(), FLOAT_TOLERANCE); assertEquals(0, mBuilder.getBrightnessReason().getModifier()); @@ -61,7 +61,7 @@ public class BrightnessLowPowerModeModifierTest { @Test public void testApply_lowPowerModeOn() { - mClamper.apply(mRequest, mBuilder); + mModifier.apply(mRequest, mBuilder); assertEquals(EXPECTED_LOW_POWER_BRIGHTNESS, mBuilder.getBrightness(), FLOAT_TOLERANCE); assertEquals(BrightnessReason.MODIFIER_LOW_POWER, @@ -73,7 +73,7 @@ public class BrightnessLowPowerModeModifierTest { public void testApply_lowPowerModeOnAndLowPowerBrightnessFactorHigh() { mRequest.screenLowPowerBrightnessFactor = 1.1f; - mClamper.apply(mRequest, mBuilder); + mModifier.apply(mRequest, mBuilder); assertEquals(DEFAULT_BRIGHTNESS, mBuilder.getBrightness(), FLOAT_TOLERANCE); assertEquals(BrightnessReason.MODIFIER_LOW_POWER, @@ -84,7 +84,7 @@ public class BrightnessLowPowerModeModifierTest { @Test public void testApply_lowPowerModeOnAndMinBrightness() { mBuilder.setBrightness(0.0f); - mClamper.apply(mRequest, mBuilder); + mModifier.apply(mRequest, mBuilder); assertEquals(0.0f, mBuilder.getBrightness(), FLOAT_TOLERANCE); assertEquals(0, mBuilder.getBrightnessReason().getModifier()); @@ -93,10 +93,10 @@ public class BrightnessLowPowerModeModifierTest { @Test public void testApply_lowPowerModeOnAndLowPowerAlreadyApplied() { - mClamper.apply(mRequest, mBuilder); + mModifier.apply(mRequest, mBuilder); DisplayBrightnessState.Builder builder = prepareBuilder(); - mClamper.apply(mRequest, builder); + mModifier.apply(mRequest, builder); assertEquals(EXPECTED_LOW_POWER_BRIGHTNESS, builder.getBrightness(), FLOAT_TOLERANCE); assertEquals(BrightnessReason.MODIFIER_LOW_POWER, @@ -106,11 +106,11 @@ public class BrightnessLowPowerModeModifierTest { @Test public void testApply_lowPowerModeOffAfterLowPowerOn() { - mClamper.apply(mRequest, mBuilder); + mModifier.apply(mRequest, mBuilder); mRequest.lowPowerMode = false; DisplayBrightnessState.Builder builder = prepareBuilder(); - mClamper.apply(mRequest, builder); + mModifier.apply(mRequest, builder); assertEquals(DEFAULT_BRIGHTNESS, builder.getBrightness(), FLOAT_TOLERANCE); assertEquals(0, builder.getBrightnessReason().getModifier()); diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/DisplayDimModifierTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/DisplayDimModifierTest.java new file mode 100644 index 000000000000..be4e7c7a9edd --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/DisplayDimModifierTest.java @@ -0,0 +1,148 @@ +/* + * 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 org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.hardware.display.DisplayManagerInternal; +import android.os.PowerManager; + +import androidx.test.filters.SmallTest; + +import com.android.internal.R; +import com.android.server.display.DisplayBrightnessState; +import com.android.server.display.brightness.BrightnessReason; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +public class DisplayDimModifierTest { + private static final float FLOAT_TOLERANCE = 0.001f; + private static final float DEFAULT_BRIGHTNESS = 0.5f; + private static final float MIN_DIM_AMOUNT = 0.05f; + private static final float DIM_CONFIG = 0.4f; + + @Mock + private Context mMockContext; + + @Mock + private PowerManager mMockPowerManager; + + @Mock + private Resources mMockResources; + + private final DisplayManagerInternal.DisplayPowerRequest + mRequest = new DisplayManagerInternal.DisplayPowerRequest(); + private final DisplayBrightnessState.Builder mBuilder = prepareBuilder(); + private DisplayDimModifier mModifier; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mMockContext.getResources()).thenReturn(mMockResources); + when(mMockResources.getFloat( + R.dimen.config_screenBrightnessMinimumDimAmountFloat)).thenReturn(MIN_DIM_AMOUNT); + when(mMockContext.getSystemService(PowerManager.class)).thenReturn(mMockPowerManager); + when(mMockPowerManager.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM)).thenReturn(DIM_CONFIG); + + mModifier = new DisplayDimModifier(mMockContext); + mRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM; + } + + @Test + public void testApply_noDimPolicy() { + mRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF; + mModifier.apply(mRequest, mBuilder); + + assertEquals(DEFAULT_BRIGHTNESS, mBuilder.getBrightness(), FLOAT_TOLERANCE); + assertEquals(0, mBuilder.getBrightnessReason().getModifier()); + assertTrue(mBuilder.isSlowChange()); + } + + @Test + public void testApply_dimPolicyFromResources() { + mBuilder.setBrightness(0.4f); + mModifier.apply(mRequest, mBuilder); + + assertEquals(0.4f - MIN_DIM_AMOUNT, mBuilder.getBrightness(), FLOAT_TOLERANCE); + assertEquals(BrightnessReason.MODIFIER_DIMMED, + mBuilder.getBrightnessReason().getModifier()); + assertFalse(mBuilder.isSlowChange()); + } + + @Test + public void testApply_dimPolicyFromConfig() { + mModifier.apply(mRequest, mBuilder); + + assertEquals(DIM_CONFIG, mBuilder.getBrightness(), FLOAT_TOLERANCE); + assertEquals(BrightnessReason.MODIFIER_DIMMED, + mBuilder.getBrightnessReason().getModifier()); + assertFalse(mBuilder.isSlowChange()); + } + + @Test + public void testApply_dimPolicyAndDimPolicyAlreadyApplied() { + mModifier.apply(mRequest, mBuilder); + DisplayBrightnessState.Builder builder = prepareBuilder(); + + mModifier.apply(mRequest, builder); + + assertEquals(DIM_CONFIG, builder.getBrightness(), FLOAT_TOLERANCE); + assertEquals(BrightnessReason.MODIFIER_DIMMED, + builder.getBrightnessReason().getModifier()); + assertTrue(builder.isSlowChange()); + } + + @Test + public void testApply_dimPolicyAndMinBrightness() { + mBuilder.setBrightness(0.0f); + mModifier.apply(mRequest, mBuilder); + + assertEquals(0.0f, mBuilder.getBrightness(), FLOAT_TOLERANCE); + assertEquals(0, mBuilder.getBrightnessReason().getModifier()); + assertFalse(mBuilder.isSlowChange()); + } + + @Test + public void testApply_dimPolicyOffAfterDimPolicyOn() { + mModifier.apply(mRequest, mBuilder); + mRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF; + DisplayBrightnessState.Builder builder = prepareBuilder(); + + mModifier.apply(mRequest, builder); + + assertEquals(DEFAULT_BRIGHTNESS, builder.getBrightness(), FLOAT_TOLERANCE); + assertEquals(0, builder.getBrightnessReason().getModifier()); + assertFalse(builder.isSlowChange()); + } + + private DisplayBrightnessState.Builder prepareBuilder() { + DisplayBrightnessState.Builder builder = DisplayBrightnessState.builder(); + builder.setBrightness(DEFAULT_BRIGHTNESS); + builder.setIsSlowChange(true); + return builder; + } +} |