diff options
| author | 2023-11-03 17:32:18 +0000 | |
|---|---|---|
| committer | 2023-11-27 16:59:00 +0000 | |
| commit | 5496dfa0eb103f7954df94aea257ce482bde184d (patch) | |
| tree | a30f5f85beecb94cf0204911cef196b3bddec314 | |
| parent | a62c4d69b5d99618d356bccd8717863f578da94b (diff) | |
Improve readability and add protections against use after release NPE
Preventively ensure the dimLayer has not been released before trying to access it.
Fix: 308448047
Test: atest CtsWindowManagerDeviceOther:android.server.wm.other.MinimalPostProcessingTests#testTwoVisibleWindowsSecondOnePrefersMinimalPostProcessing --iteration 100
Test: atest DimmerTests
Change-Id: Id08838e0a98ba949382442b8fac8e81156d1aa40
5 files changed, 475 insertions, 331 deletions
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index dc2b0561957d..9d76c9f5fc93 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -535,6 +535,12 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/ActivityStarter.java" }, + "-1582845629": { + "message": "Starting animation on %s", + "level": "VERBOSE", + "group": "WM_DEBUG_DIMMER", + "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java" + }, "-1575977269": { "message": "Skipping %s: mismatch root %s", "level": "DEBUG", @@ -925,6 +931,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "-1243510456": { + "message": "Dim animation requested: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_DIMMER", + "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java" + }, "-1237827119": { "message": "Schedule remove starting %s startingWindow=%s animate=%b Callers=%s", "level": "VERBOSE", @@ -1177,6 +1189,12 @@ "group": "WM_DEBUG_BACK_PREVIEW", "at": "com\/android\/server\/wm\/BackNavigationController.java" }, + "-1028213464": { + "message": "%s skipping animation and directly setting alpha=%f, blur=%d", + "level": "DEBUG", + "group": "WM_DEBUG_DIMMER", + "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java" + }, "-1022146708": { "message": "Skipping %s: mismatch activity type", "level": "DEBUG", @@ -1807,12 +1825,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-504637678": { - "message": "Starting animation on dim layer %s, requested by %s, alpha: %f -> %f, blur: %d -> %d", - "level": "VERBOSE", - "group": "WM_DEBUG_DIMMER", - "at": "com\/android\/server\/wm\/SmoothDimmer.java" - }, "-503656156": { "message": "Update process config of %s to new config %s", "level": "VERBOSE", @@ -2731,12 +2743,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "309039362": { - "message": "SURFACE MATRIX [%f,%f,%f,%f]: %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowSurfaceController.java" - }, "312030608": { "message": "New topFocusedDisplayId=%d", "level": "DEBUG", @@ -3091,12 +3097,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "633654009": { - "message": "SURFACE POS (setPositionInTransaction) @ (%f,%f): %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowSurfaceController.java" - }, "638429464": { "message": "\tRemove container=%s", "level": "DEBUG", @@ -4039,12 +4039,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "1620751818": { - "message": "Dim %s skipping animation and directly setting alpha=%f, blur=%d", - "level": "DEBUG", - "group": "WM_DEBUG_DIMMER", - "at": "com\/android\/server\/wm\/SmoothDimmer.java" - }, "1621562070": { "message": " startWCT=%s", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java index 2fabb0ea686a..7ce9de4e1c24 100644 --- a/services/core/java/com/android/server/wm/Dimmer.java +++ b/services/core/java/com/android/server/wm/Dimmer.java @@ -83,6 +83,7 @@ public abstract class Dimmer { /** * Mark all dims as pending completion on the next call to {@link #updateDims} * + * Called before iterating on mHost's children, first step of dimming. * This is intended for us by the host container, to be called at the beginning of * {@link WindowContainer#prepareSurfaces}. After calling this, the container should * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them @@ -100,8 +101,7 @@ public abstract class Dimmer { /** * Call after invoking {@link WindowContainer#prepareSurfaces} on children as - * described in {@link #resetDimStates}. The dim bounds returned by {@link #resetDimStates} - * should be set before calling this method. + * described in {@link #resetDimStates}. * * @param t A transaction in which to update the dims. * @return true if any Dims were updated. diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java new file mode 100644 index 000000000000..e91857f1da82 --- /dev/null +++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java @@ -0,0 +1,334 @@ +/* + * 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.wm; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER; +import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS; +import static com.android.server.wm.AlphaAnimationSpecProto.FROM; +import static com.android.server.wm.AlphaAnimationSpecProto.TO; +import static com.android.server.wm.AnimationSpecProto.ALPHA; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + +import android.util.Log; +import android.util.proto.ProtoOutputStream; +import android.view.SurfaceControl; + +import com.android.internal.protolog.common.ProtoLog; + +import java.io.PrintWriter; + +/** + * Contains the information relative to the changes to apply to the dim layer + */ +public class DimmerAnimationHelper { + private static final String TAG = TAG_WITH_CLASS_NAME ? "DimmerAnimationHelper" : TAG_WM; + private static final int DEFAULT_DIM_ANIM_DURATION_MS = 200; + + /** + * Contains the requested changes + */ + static class Change { + private float mAlpha = -1f; + private int mBlurRadius = -1; + private WindowContainer mDimmingContainer = null; + private int mRelativeLayer = -1; + private static final float EPSILON = 0.0001f; + + Change() {} + + Change(Change other) { + mAlpha = other.mAlpha; + mBlurRadius = other.mBlurRadius; + mDimmingContainer = other.mDimmingContainer; + mRelativeLayer = other.mRelativeLayer; + } + + // Same alpha and blur + boolean hasSameVisualProperties(Change other) { + return Math.abs(mAlpha - other.mAlpha) < EPSILON && mBlurRadius == other.mBlurRadius; + } + + boolean hasSameDimmingContainer(Change other) { + return mDimmingContainer != null && mDimmingContainer == other.mDimmingContainer; + } + + void inheritPropertiesFromAnimation(AnimationSpec anim) { + mAlpha = anim.mCurrentAlpha; + mBlurRadius = anim.mCurrentBlur; + } + + @Override + public String toString() { + return "Dim state: alpha=" + mAlpha + ", blur=" + mBlurRadius + ", container=" + + mDimmingContainer + ", relativePosition=" + mRelativeLayer; + } + } + + private Change mCurrentProperties = new Change(); + private Change mRequestedProperties = new Change(); + private AnimationSpec mAlphaAnimationSpec; + + private final AnimationAdapterFactory mAnimationAdapterFactory; + private AnimationAdapter mLocalAnimationAdapter; + + DimmerAnimationHelper(AnimationAdapterFactory animationFactory) { + mAnimationAdapterFactory = animationFactory; + } + + void setExitParameters() { + setRequestedRelativeParent(mRequestedProperties.mDimmingContainer, -1 /* relativeLayer */); + setRequestedAppearance(0f /* alpha */, 0 /* blur */); + } + + // Sets a requested change without applying it immediately + void setRequestedRelativeParent(WindowContainer relativeParent, int relativeLayer) { + mRequestedProperties.mDimmingContainer = relativeParent; + mRequestedProperties.mRelativeLayer = relativeLayer; + } + + // Sets a requested change without applying it immediately + void setRequestedAppearance(float alpha, int blurRadius) { + mRequestedProperties.mAlpha = alpha; + mRequestedProperties.mBlurRadius = blurRadius; + } + + /** + * Commit the last changes we received. Called after + * {@link Change#setExitParameters()}, + * {@link Change#setRequestedRelativeParent(WindowContainer, int)}, or + * {@link Change#setRequestedAppearance(float, int)} + */ + void applyChanges(SurfaceControl.Transaction t, SmoothDimmer.DimState dim) { + if (mRequestedProperties.mDimmingContainer == null) { + Log.e(TAG, this + " does not have a dimming container. Have you forgotten to " + + "call adjustRelativeLayer?"); + return; + } + if (mRequestedProperties.mDimmingContainer.mSurfaceControl == null) { + Log.w(TAG, "container " + mRequestedProperties.mDimmingContainer + + "does not have a surface"); + dim.remove(t); + return; + } + + dim.ensureVisible(t); + relativeReparent(dim.mDimSurface, + mRequestedProperties.mDimmingContainer.getSurfaceControl(), + mRequestedProperties.mRelativeLayer, t); + + if (!mCurrentProperties.hasSameVisualProperties(mRequestedProperties)) { + stopCurrentAnimation(dim.mDimSurface); + + if (dim.mSkipAnimation + // If the container doesn't change but requests a dim change, then it is + // directly providing us the animated values + || (mRequestedProperties.hasSameDimmingContainer(mCurrentProperties) + && dim.isDimming())) { + ProtoLog.d(WM_DEBUG_DIMMER, + "%s skipping animation and directly setting alpha=%f, blur=%d", + dim, mRequestedProperties.mAlpha, + mRequestedProperties.mBlurRadius); + setAlphaBlur(dim.mDimSurface, mRequestedProperties.mAlpha, + mRequestedProperties.mBlurRadius, t); + dim.mSkipAnimation = false; + } else { + startAnimation(t, dim); + } + + } else if (!dim.isDimming()) { + // We are not dimming, so we tried the exit animation but the alpha is already 0, + // therefore, let's just remove this surface + dim.remove(t); + } + mCurrentProperties = new Change(mRequestedProperties); + } + + private void startAnimation( + SurfaceControl.Transaction t, SmoothDimmer.DimState dim) { + ProtoLog.v(WM_DEBUG_DIMMER, "Starting animation on %s", dim); + mAlphaAnimationSpec = getRequestedAnimationSpec(); + mLocalAnimationAdapter = mAnimationAdapterFactory.get(mAlphaAnimationSpec, + dim.mHostContainer.mWmService.mSurfaceAnimationRunner); + + float targetAlpha = mRequestedProperties.mAlpha; + int targetBlur = mRequestedProperties.mBlurRadius; + + mLocalAnimationAdapter.startAnimation(dim.mDimSurface, t, + ANIMATION_TYPE_DIMMER, /* finishCallback */ (type, animator) -> { + setAlphaBlur(dim.mDimSurface, targetAlpha, targetBlur, t); + if (targetAlpha == 0f && !dim.isDimming()) { + dim.remove(t); + } + mLocalAnimationAdapter = null; + mAlphaAnimationSpec = null; + }); + } + + private boolean isAnimating() { + return mAlphaAnimationSpec != null; + } + + void stopCurrentAnimation(SurfaceControl surface) { + if (mLocalAnimationAdapter != null && isAnimating()) { + // Save the current animation progress and cancel the animation + mCurrentProperties.inheritPropertiesFromAnimation(mAlphaAnimationSpec); + mLocalAnimationAdapter.onAnimationCancelled(surface); + mLocalAnimationAdapter = null; + mAlphaAnimationSpec = null; + } + } + + private AnimationSpec getRequestedAnimationSpec() { + final float startAlpha = Math.max(mCurrentProperties.mAlpha, 0f); + final int startBlur = Math.max(mCurrentProperties.mBlurRadius, 0); + long duration = (long) (getDimDuration(mRequestedProperties.mDimmingContainer) + * Math.abs(mRequestedProperties.mAlpha - startAlpha)); + + final AnimationSpec spec = new AnimationSpec( + new AnimationSpec.AnimationExtremes<>(startAlpha, mRequestedProperties.mAlpha), + new AnimationSpec.AnimationExtremes<>(startBlur, mRequestedProperties.mBlurRadius), + duration + ); + ProtoLog.v(WM_DEBUG_DIMMER, "Dim animation requested: %s", spec); + return spec; + } + + /** + * Change the relative parent of this dim layer + */ + void relativeReparent(SurfaceControl dimLayer, SurfaceControl relativeParent, + int relativePosition, SurfaceControl.Transaction t) { + try { + t.setRelativeLayer(dimLayer, relativeParent, relativePosition); + } catch (NullPointerException e) { + Log.w(TAG, "Tried to change parent of dim " + dimLayer + " after remove", e); + } + } + + void setAlphaBlur(SurfaceControl sc, float alpha, int blur, SurfaceControl.Transaction t) { + try { + t.setAlpha(sc, alpha); + t.setBackgroundBlurRadius(sc, blur); + } catch (NullPointerException e) { + Log.w(TAG , "Tried to change look of dim " + sc + " after remove", e); + } + } + + private long getDimDuration(WindowContainer container) { + // Use the same duration as the animation on the WindowContainer + AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation(); + final float durationScale = container.mWmService.getTransitionAnimationScaleLocked(); + return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION_MS * durationScale) + : animationAdapter.getDurationHint(); + } + + /** + * Collects the animation specifics + */ + static class AnimationSpec implements LocalAnimationAdapter.AnimationSpec { + private static final String TAG = TAG_WITH_CLASS_NAME ? "DimmerAnimationSpec" : TAG_WM; + + static class AnimationExtremes<T> { + final T mStartValue; + final T mFinishValue; + + AnimationExtremes(T fromValue, T toValue) { + mStartValue = fromValue; + mFinishValue = toValue; + } + + @Override + public String toString() { + return "[" + mStartValue + "->" + mFinishValue + "]"; + } + } + + private final long mDuration; + private final AnimationSpec.AnimationExtremes<Float> mAlpha; + private final AnimationSpec.AnimationExtremes<Integer> mBlur; + + float mCurrentAlpha = 0; + int mCurrentBlur = 0; + boolean mStarted = false; + + AnimationSpec(AnimationSpec.AnimationExtremes<Float> alpha, + AnimationSpec.AnimationExtremes<Integer> blur, long duration) { + mAlpha = alpha; + mBlur = blur; + mDuration = duration; + } + + @Override + public long getDuration() { + return mDuration; + } + + @Override + public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) { + if (!mStarted) { + // The first frame would end up in the sync transaction, and since this could be + // applied after the animation transaction, we avoid putting visible changes here. + // The initial state of the animation matches the current state of the dim anyway. + mStarted = true; + return; + } + final float fraction = getFraction(currentPlayTime); + mCurrentAlpha = + fraction * (mAlpha.mFinishValue - mAlpha.mStartValue) + mAlpha.mStartValue; + mCurrentBlur = + (int) fraction * (mBlur.mFinishValue - mBlur.mStartValue) + mBlur.mStartValue; + if (sc.isValid()) { + t.setAlpha(sc, mCurrentAlpha); + t.setBackgroundBlurRadius(sc, mCurrentBlur); + } else { + Log.w(TAG, "Dimmer#AnimationSpec tried to access " + sc + " after release"); + } + } + + @Override + public String toString() { + return "Animation spec: alpha=" + mAlpha + ", blur=" + mBlur; + } + + @Override + public void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("from_alpha="); pw.print(mAlpha.mStartValue); + pw.print(" to_alpha="); pw.print(mAlpha.mFinishValue); + pw.print(prefix); pw.print("from_blur="); pw.print(mBlur.mStartValue); + pw.print(" to_blur="); pw.print(mBlur.mFinishValue); + pw.print(" duration="); pw.println(mDuration); + } + + @Override + public void dumpDebugInner(ProtoOutputStream proto) { + final long token = proto.start(ALPHA); + proto.write(FROM, mAlpha.mStartValue); + proto.write(TO, mAlpha.mFinishValue); + proto.write(DURATION_MS, mDuration); + proto.end(token); + } + } + + static class AnimationAdapterFactory { + public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec, + SurfaceAnimationRunner runner) { + return new LocalAnimationAdapter(alphaAnimationSpec, runner); + } + } +} diff --git a/services/core/java/com/android/server/wm/SmoothDimmer.java b/services/core/java/com/android/server/wm/SmoothDimmer.java index 2549bbf70e9f..b5d94a2efbdf 100644 --- a/services/core/java/com/android/server/wm/SmoothDimmer.java +++ b/services/core/java/com/android/server/wm/SmoothDimmer.java @@ -17,397 +17,212 @@ package com.android.server.wm; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER; -import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS; -import static com.android.server.wm.AlphaAnimationSpecProto.FROM; -import static com.android.server.wm.AlphaAnimationSpecProto.TO; -import static com.android.server.wm.AnimationSpecProto.ALPHA; -import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.graphics.Rect; import android.util.Log; -import android.util.proto.ProtoOutputStream; import android.view.Surface; import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; -import java.io.PrintWriter; - class SmoothDimmer extends Dimmer { + private static final String TAG = TAG_WITH_CLASS_NAME ? "Dimmer" : TAG_WM; - private static final float EPSILON = 0.0001f; - // This is in milliseconds. - private static final int DEFAULT_DIM_ANIM_DURATION = 200; DimState mDimState; - private WindowContainer mLastRequestedDimContainer; - private final AnimationAdapterFactory mAnimationAdapterFactory; + final DimmerAnimationHelper.AnimationAdapterFactory mAnimationAdapterFactory; + /** + * Controls the dim behaviour + */ @VisibleForTesting class DimState { - /** - * The layer where property changes should be invoked on. - */ - SurfaceControl mDimLayer; - boolean mDimming; - boolean mIsVisible; - + /** Related objects */ + SurfaceControl mDimSurface; + final WindowContainer mHostContainer; + // The last container to request to dim + private WindowContainer mLastRequestedDimContainer; + /** Animation */ + private final DimmerAnimationHelper mAnimationHelper; + boolean mSkipAnimation = false; + // Determines whether the dim layer should animate before destroying. + boolean mAnimateExit = true; + /** Surface visibility and bounds */ + private boolean mIsVisible = false; // TODO(b/64816140): Remove after confirming dimmer layer always matches its container. final Rect mDimBounds = new Rect(); - /** - * Determines whether the dim layer should animate before destroying. - */ - boolean mAnimateExit = true; - - /** - * Used for Dims not associated with a WindowContainer. - * See {@link Dimmer#adjustRelativeLayer(WindowContainer, int)} for details on Dim - * lifecycle. - */ - boolean mDontReset; - - Change mCurrentProperties; - Change mRequestedProperties; - private AnimationSpec mAlphaAnimationSpec; - private AnimationAdapter mLocalAnimationAdapter; - - static class Change { - private float mAlpha = -1f; - private int mBlurRadius = -1; - private WindowContainer mDimmingContainer = null; - private int mRelativeLayer = -1; - private boolean mSkipAnimation = false; - - Change() {} - - Change(Change other) { - mAlpha = other.mAlpha; - mBlurRadius = other.mBlurRadius; - mDimmingContainer = other.mDimmingContainer; - mRelativeLayer = other.mRelativeLayer; - } - - @Override - public String toString() { - return "Dim state: alpha=" + mAlpha + ", blur=" + mBlurRadius + ", container=" - + mDimmingContainer + ", relativePosition=" + mRelativeLayer - + ", skipAnimation=" + mSkipAnimation; + DimState() { + mHostContainer = mHost; + mAnimationHelper = new DimmerAnimationHelper(mAnimationAdapterFactory); + try { + mDimSurface = makeDimLayer(); + } catch (Surface.OutOfResourcesException e) { + Log.w(TAG, "OutOfResourcesException creating dim surface"); } } - DimState(SurfaceControl dimLayer) { - mDimLayer = dimLayer; - mDimming = true; - mCurrentProperties = new Change(); - mRequestedProperties = new Change(); - } - - void setExitParameters(WindowContainer container) { - setRequestedRelativeParent(container, -1 /* relativeLayer */); - setRequestedAppearance(0f /* alpha */, 0 /* blur */); + void ensureVisible(SurfaceControl.Transaction t) { + if (!mIsVisible) { + t.show(mDimSurface); + t.setAlpha(mDimSurface, 0f); + mIsVisible = true; + } } - // Sets a requested change without applying it immediately - void setRequestedRelativeParent(WindowContainer relativeParent, int relativeLayer) { - mRequestedProperties.mDimmingContainer = relativeParent; - mRequestedProperties.mRelativeLayer = relativeLayer; + void adjustSurfaceLayout(SurfaceControl.Transaction t) { + // TODO: Once we use geometry from hierarchy this falls away. + t.setPosition(mDimSurface, mDimBounds.left, mDimBounds.top); + t.setWindowCrop(mDimSurface, mDimBounds.width(), mDimBounds.height()); } - // Sets a requested change without applying it immediately - void setRequestedAppearance(float alpha, int blurRadius) { - mRequestedProperties.mAlpha = alpha; - mRequestedProperties.mBlurRadius = blurRadius; + /** + * Set the parameters to prepare the dim to change its appearance + */ + void prepareLookChange(float alpha, int blurRadius) { + mAnimationHelper.setRequestedAppearance(alpha, blurRadius); } /** - * Commit the last changes we received. Called after - * {@link Change#setExitParameters(WindowContainer)}, - * {@link Change#setRequestedRelativeParent(WindowContainer, int)}, or - * {@link Change#setRequestedAppearance(float, int)} + * Prepare the dim for the exit animation */ - void applyChanges(SurfaceControl.Transaction t) { - if (mRequestedProperties.mDimmingContainer == null) { - Log.e(TAG, this + " does not have a dimming container. Have you forgotten to " - + "call adjustRelativeLayer?"); - return; - } - if (mRequestedProperties.mDimmingContainer.mSurfaceControl == null) { - Log.w(TAG, "container " + mRequestedProperties.mDimmingContainer - + "does not have a surface"); - return; - } - if (!mDimState.mIsVisible) { - mDimState.mIsVisible = true; - t.show(mDimState.mDimLayer); + void exit(SurfaceControl.Transaction t) { + if (!mAnimateExit) { + remove(t); + } else { + mAnimationHelper.setExitParameters(); + setReady(t); } - t.setRelativeLayer(mDimLayer, - mRequestedProperties.mDimmingContainer.getSurfaceControl(), - mRequestedProperties.mRelativeLayer); + } - if (aspectChanged()) { - if (isAnimating()) { - mLocalAnimationAdapter.onAnimationCancelled(mDimLayer); - } - if (mRequestedProperties.mSkipAnimation - || (!dimmingContainerChanged() && mDimming)) { - // If the dimming container has not changed, then it is running its own - // animation, thus we can directly set the values we get requested, unless it's - // the exiting animation - ProtoLog.d(WM_DEBUG_DIMMER, - "Dim %s skipping animation and directly setting alpha=%f, blur=%d", - mDimLayer, mRequestedProperties.mAlpha, - mRequestedProperties.mBlurRadius); - t.setAlpha(mDimLayer, mRequestedProperties.mAlpha); - t.setBackgroundBlurRadius(mDimLayer, mRequestedProperties.mBlurRadius); - mRequestedProperties.mSkipAnimation = false; - } else { - startAnimation(t); - } + void remove(SurfaceControl.Transaction t) { + mAnimationHelper.stopCurrentAnimation(mDimSurface); + if (mDimSurface.isValid()) { + t.remove(mDimSurface); + ProtoLog.d(WM_DEBUG_DIMMER, + "Removing dim surface %s on transaction %s", this, t); + } else { + Log.w(TAG, "Tried to remove " + mDimSurface + " multiple times\n"); } - mCurrentProperties = new Change(mRequestedProperties); } - private void startAnimation(SurfaceControl.Transaction t) { - mAlphaAnimationSpec = getRequestedAnimationSpec(mRequestedProperties.mAlpha, - mRequestedProperties.mBlurRadius); - mLocalAnimationAdapter = mAnimationAdapterFactory.get(mAlphaAnimationSpec, - mHost.mWmService.mSurfaceAnimationRunner); - - mLocalAnimationAdapter.startAnimation(mDimLayer, t, - ANIMATION_TYPE_DIMMER, (type, animator) -> { - t.setAlpha(mDimLayer, mRequestedProperties.mAlpha); - t.setBackgroundBlurRadius(mDimLayer, mRequestedProperties.mBlurRadius); - if (mRequestedProperties.mAlpha == 0f && !mDimming) { - ProtoLog.d(WM_DEBUG_DIMMER, - "Removing dim surface %s on transaction %s", mDimLayer, t); - t.remove(mDimLayer); - } - mLocalAnimationAdapter = null; - mAlphaAnimationSpec = null; - }); + @Override + public String toString() { + return "SmoothDimmer#DimState with host=" + mHostContainer + ", surface=" + mDimSurface; } - private boolean isAnimating() { - return mAlphaAnimationSpec != null; + /** + * Set the parameters to prepare the dim to be relative parented to the dimming container + */ + void prepareReparent(WindowContainer relativeParent, int relativeLayer) { + mAnimationHelper.setRequestedRelativeParent(relativeParent, relativeLayer); } - private boolean aspectChanged() { - return Math.abs(mRequestedProperties.mAlpha - mCurrentProperties.mAlpha) > EPSILON - || mRequestedProperties.mBlurRadius != mCurrentProperties.mBlurRadius; + /** + * Call when all the changes have been requested to have them applied + * @param t The transaction in which to apply the changes + */ + void setReady(SurfaceControl.Transaction t) { + mAnimationHelper.applyChanges(t, this); } - private boolean dimmingContainerChanged() { - return mRequestedProperties.mDimmingContainer != mCurrentProperties.mDimmingContainer; + /** + * Whether anyone is currently requesting the dim + */ + boolean isDimming() { + return mLastRequestedDimContainer != null; } - private AnimationSpec getRequestedAnimationSpec(float targetAlpha, int targetBlur) { - final float startAlpha; - final int startBlur; - if (mAlphaAnimationSpec != null) { - startAlpha = mAlphaAnimationSpec.mCurrentAlpha; - startBlur = mAlphaAnimationSpec.mCurrentBlur; - } else { - startAlpha = Math.max(mCurrentProperties.mAlpha, 0f); - startBlur = Math.max(mCurrentProperties.mBlurRadius, 0); - } - long duration = (long) (getDimDuration(mRequestedProperties.mDimmingContainer) - * Math.abs(targetAlpha - startAlpha)); - - ProtoLog.v(WM_DEBUG_DIMMER, "Starting animation on dim layer %s, requested by %s, " - + "alpha: %f -> %f, blur: %d -> %d", - mDimLayer, mRequestedProperties.mDimmingContainer, startAlpha, targetAlpha, - startBlur, targetBlur); - return new AnimationSpec( - new AnimationExtremes<>(startAlpha, targetAlpha), - new AnimationExtremes<>(startBlur, targetBlur), - duration - ); + private SurfaceControl makeDimLayer() { + return mHost.makeChildSurface(null) + .setParent(mHost.getSurfaceControl()) + .setColorLayer() + .setName("Dim Layer for - " + mHost.getName()) + .setCallsite("DimLayer.makeDimLayer") + .build(); } } protected SmoothDimmer(WindowContainer host) { - this(host, new AnimationAdapterFactory()); + this(host, new DimmerAnimationHelper.AnimationAdapterFactory()); } @VisibleForTesting - SmoothDimmer(WindowContainer host, AnimationAdapterFactory animationFactory) { + SmoothDimmer(WindowContainer host, + DimmerAnimationHelper.AnimationAdapterFactory animationFactory) { super(host); mAnimationAdapterFactory = animationFactory; } - private DimState obtainDimState(WindowContainer container) { - if (mDimState == null) { - try { - final SurfaceControl ctl = makeDimLayer(); - mDimState = new DimState(ctl); - } catch (Surface.OutOfResourcesException e) { - Log.w(TAG, "OutOfResourcesException creating dim surface"); - } - } - - mLastRequestedDimContainer = container; - return mDimState; - } - - private SurfaceControl makeDimLayer() { - return mHost.makeChildSurface(null) - .setParent(mHost.getSurfaceControl()) - .setColorLayer() - .setName("Dim Layer for - " + mHost.getName()) - .setCallsite("Dimmer.makeDimLayer") - .build(); - } - - @Override - SurfaceControl getDimLayer() { - return mDimState != null ? mDimState.mDimLayer : null; - } - @Override void resetDimStates() { - if (mDimState == null) { - return; - } - if (!mDimState.mDontReset) { - mDimState.mDimming = false; - } - } - - @Override - Rect getDimBounds() { - return mDimState != null ? mDimState.mDimBounds : null; - } - - @Override - void dontAnimateExit() { if (mDimState != null) { - mDimState.mAnimateExit = false; + mDimState.mLastRequestedDimContainer = null; } } @Override protected void adjustAppearance(WindowContainer container, float alpha, int blurRadius) { final DimState d = obtainDimState(container); - mDimState.setRequestedAppearance(alpha, blurRadius); - d.mDimming = true; + d.prepareLookChange(alpha, blurRadius); } @Override protected void adjustRelativeLayer(WindowContainer container, int relativeLayer) { if (mDimState != null) { - mDimState.setRequestedRelativeParent(container, relativeLayer); + mDimState.prepareReparent(container, relativeLayer); } } + @Override boolean updateDims(SurfaceControl.Transaction t) { if (mDimState == null) { return false; } - - if (!mDimState.mDimming) { - // No one is dimming anymore, fade out dim and remove - if (!mDimState.mAnimateExit) { - if (mDimState.mDimLayer.isValid()) { - t.remove(mDimState.mDimLayer); - } - } else { - mDimState.setExitParameters( - mDimState.mRequestedProperties.mDimmingContainer); - mDimState.applyChanges(t); - } + if (!mDimState.isDimming()) { + // No one is dimming, fade out and remove the dim + mDimState.exit(t); mDimState = null; return false; + } else { + // Someone is dimming, show the requested changes + mDimState.adjustSurfaceLayout(t); + final WindowState ws = mDimState.mLastRequestedDimContainer.asWindowState(); + if (!mDimState.mIsVisible && ws != null && ws.mActivityRecord != null + && ws.mActivityRecord.mStartingData != null) { + // Skip enter animation while starting window is on top of its activity + mDimState.mSkipAnimation = true; + } + mDimState.setReady(t); + return true; } - final Rect bounds = mDimState.mDimBounds; - // TODO: Once we use geometry from hierarchy this falls away. - t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top); - t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height()); - // Skip enter animation while starting window is on top of its activity - final WindowState ws = mLastRequestedDimContainer.asWindowState(); - if (!mDimState.mIsVisible && ws != null && ws.mActivityRecord != null - && ws.mActivityRecord.mStartingData != null) { - mDimState.mRequestedProperties.mSkipAnimation = true; - } - mDimState.applyChanges(t); - return true; - } - - private long getDimDuration(WindowContainer container) { - // Use the same duration as the animation on the WindowContainer - AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation(); - final float durationScale = container.mWmService.getTransitionAnimationScaleLocked(); - return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION * durationScale) - : animationAdapter.getDurationHint(); } - private static class AnimationExtremes<T> { - final T mStartValue; - final T mFinishValue; - - AnimationExtremes(T fromValue, T toValue) { - mStartValue = fromValue; - mFinishValue = toValue; + private DimState obtainDimState(WindowContainer container) { + if (mDimState == null) { + mDimState = new DimState(); } + mDimState.mLastRequestedDimContainer = container; + return mDimState; } - private static class AnimationSpec implements LocalAnimationAdapter.AnimationSpec { - private final long mDuration; - private final AnimationExtremes<Float> mAlpha; - private final AnimationExtremes<Integer> mBlur; - - float mCurrentAlpha = 0; - int mCurrentBlur = 0; - - AnimationSpec(AnimationExtremes<Float> alpha, - AnimationExtremes<Integer> blur, long duration) { - mAlpha = alpha; - mBlur = blur; - mDuration = duration; - } - - @Override - public long getDuration() { - return mDuration; - } - - @Override - public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) { - final float fraction = getFraction(currentPlayTime); - mCurrentAlpha = - fraction * (mAlpha.mFinishValue - mAlpha.mStartValue) + mAlpha.mStartValue; - mCurrentBlur = - (int) fraction * (mBlur.mFinishValue - mBlur.mStartValue) + mBlur.mStartValue; - t.setAlpha(sc, mCurrentAlpha); - t.setBackgroundBlurRadius(sc, mCurrentBlur); - } - - @Override - public void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("from_alpha="); pw.print(mAlpha.mStartValue); - pw.print(" to_alpha="); pw.print(mAlpha.mFinishValue); - pw.print(prefix); pw.print("from_blur="); pw.print(mBlur.mStartValue); - pw.print(" to_blur="); pw.print(mBlur.mFinishValue); - pw.print(" duration="); pw.println(mDuration); - } - - @Override - public void dumpDebugInner(ProtoOutputStream proto) { - final long token = proto.start(ALPHA); - proto.write(FROM, mAlpha.mStartValue); - proto.write(TO, mAlpha.mFinishValue); - proto.write(DURATION_MS, mDuration); - proto.end(token); - } + @Override + @VisibleForTesting + SurfaceControl getDimLayer() { + return mDimState != null ? mDimState.mDimSurface : null; } - static class AnimationAdapterFactory { + @Override + Rect getDimBounds() { + return mDimState != null ? mDimState.mDimBounds : null; + } - public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec, - SurfaceAnimationRunner runner) { - return new LocalAnimationAdapter(alphaAnimationSpec, runner); + @Override + void dontAnimateExit() { + if (mDimState != null) { + mDimState.mAnimateExit = false; } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java index 9f584911aed7..a831f6a382ed 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java @@ -122,7 +122,8 @@ public class DimmerTests extends WindowTestsBase { } } - static class MockAnimationAdapterFactory extends SmoothDimmer.AnimationAdapterFactory { + static class MockAnimationAdapterFactory extends DimmerAnimationHelper.AnimationAdapterFactory { + @Override public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec, SurfaceAnimationRunner runner) { return sTestAnimation; |