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

Commit e0bf258d authored by Marzia Favaro's avatar Marzia Favaro Committed by Android (Google) Code Review
Browse files

Merge "Improve readability and add protections against use after release NPE" into main

parents 3b565b4e 5496dfa0
Loading
Loading
Loading
Loading
+18 −12
Original line number Diff line number Diff line
@@ -529,6 +529,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",
@@ -1171,6 +1183,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",
@@ -1795,12 +1813,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",
@@ -4027,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",
+2 −2
Original line number Diff line number Diff line
@@ -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.
+334 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.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);
        }
    }
}
+119 −304

File changed.

Preview size limit exceeded, changes collapsed.

+2 −1
Original line number Diff line number Diff line
@@ -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;