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

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

Merge "Apply dim changes only at the end of traversal" into main

parents fffc7a53 6321bda5
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -13,3 +13,10 @@ flag {
  description: "On close to square display, when necessary, configuration includes status bar"
  bug: "291870756"
}

flag {
  name: "dimmer_refactor"
  namespace: "windowing_frontend"
  description: "Refactor dim to fix flickers"
  bug: "281632483,295291019"
}
 No newline at end of file
+2 −0
Original line number Diff line number Diff line
@@ -91,6 +91,8 @@ public enum ProtoLogGroup implements IProtoLogGroup {
    WM_DEBUG_BACK_PREVIEW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
            "CoreBackPreview"),
    WM_DEBUG_DREAM(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, Consts.TAG_WM),

    WM_DEBUG_DIMMER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
    TEST_GROUP(true, true, false, "WindowManagerProtoLogTest");

    private final boolean mEnabled;
+21 −0
Original line number Diff line number Diff line
@@ -1801,6 +1801,12 @@
      "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",
@@ -3739,6 +3745,12 @@
      "group": "WM_DEBUG_CONFIGURATION",
      "at": "com\/android\/server\/wm\/ActivityClientController.java"
    },
    "1309365288": {
      "message": "Removing dim surface %s on transaction %s",
      "level": "DEBUG",
      "group": "WM_DEBUG_DIMMER",
      "at": "com\/android\/server\/wm\/SmoothDimmer.java"
    },
    "1316533291": {
      "message": "State movement: %s from:%s to:%s reason:%s",
      "level": "VERBOSE",
@@ -4003,6 +4015,12 @@
      "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",
@@ -4560,6 +4578,9 @@
    "WM_DEBUG_CONTENT_RECORDING": {
      "tag": "WindowManager"
    },
    "WM_DEBUG_DIMMER": {
      "tag": "WindowManager"
    },
    "WM_DEBUG_DRAW": {
      "tag": "WindowManager"
    },
+3 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.animation.Animation;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;

@@ -31,7 +32,8 @@ import java.io.PrintWriter;
 * Interface that describes an animation and bridges the animation start to the component
 * responsible for running the animation.
 */
interface AnimationAdapter {
@VisibleForTesting
public interface AnimationAdapter {

    long STATUS_BAR_TRANSITION_DURATION = 120L;

+20 −311
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 * 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.
@@ -11,172 +11,36 @@
 * 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
 * limitations under the License.
 */

package com.android.server.wm;

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 android.annotation.NonNull;
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.server.wm.SurfaceAnimator.AnimationType;

import java.io.PrintWriter;
import com.android.window.flags.Flags;

/**
 * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
 * black layers of varying opacity at various Z-levels which create the effect of a Dim.
 */
class Dimmer {
    private static final String TAG = "WindowManager";
    // This is in milliseconds.
    private static final int DEFAULT_DIM_ANIM_DURATION = 200;

    private class DimAnimatable implements SurfaceAnimator.Animatable {
        private SurfaceControl mDimLayer;

        private DimAnimatable(SurfaceControl dimLayer) {
            mDimLayer = dimLayer;
        }

        @Override
        public SurfaceControl.Transaction getSyncTransaction() {
            return mHost.getSyncTransaction();
        }

        @Override
        public SurfaceControl.Transaction getPendingTransaction() {
            return mHost.getPendingTransaction();
        }

        @Override
        public void commitPendingTransaction() {
            mHost.commitPendingTransaction();
        }

        @Override
        public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
        }

        @Override
        public void onAnimationLeashLost(SurfaceControl.Transaction t) {
        }

        @Override
        public SurfaceControl.Builder makeAnimationLeash() {
            return mHost.makeAnimationLeash();
        }

        @Override
        public SurfaceControl getAnimationLeashParent() {
            return mHost.getSurfaceControl();
        }

        @Override
        public SurfaceControl getSurfaceControl() {
            return mDimLayer;
        }

        @Override
        public SurfaceControl getParentSurfaceControl() {
            return mHost.getSurfaceControl();
        }

        @Override
        public int getSurfaceWidth() {
            // This will determine the size of the leash created. This should be the size of the
            // host and not the dim layer since the dim layer may get bigger during animation. If
            // that occurs, the leash size cannot change so we need to ensure the leash is big
            // enough that the dim layer can grow.
            // This works because the mHost will be a Task which has the display bounds.
            return mHost.getSurfaceWidth();
        }

        @Override
        public int getSurfaceHeight() {
            // See getSurfaceWidth() above for explanation.
            return mHost.getSurfaceHeight();
        }

        void removeSurface() {
            if (mDimLayer != null && mDimLayer.isValid()) {
                getSyncTransaction().remove(mDimLayer);
            }
            mDimLayer = null;
        }
    }

    @VisibleForTesting
    class DimState {
        /**
         * The layer where property changes should be invoked on.
         */
        SurfaceControl mDimLayer;
        boolean mDimming;
        boolean isVisible;
        SurfaceAnimator mSurfaceAnimator;

        // 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;

public abstract class Dimmer {
    /**
         * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
         * details on Dim lifecycle.
         */
        boolean mDontReset;

        DimState(SurfaceControl dimLayer) {
            mDimLayer = dimLayer;
            mDimming = true;
            final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer);
            mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, (type, anim) -> {
                if (!mDimming) {
                    dimAnimatable.removeSurface();
                }
            }, mHost.mWmService);
        }
    }

    /**
     * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
     * The {@link WindowContainer} that our Dims are bounded to. We may be dimming on behalf of the
     * host, some controller of it, or one of the hosts children.
     */
    private WindowContainer mHost;
    private WindowContainer mLastRequestedDimContainer;
    @VisibleForTesting
    DimState mDimState;

    private final SurfaceAnimatorStarter mSurfaceAnimatorStarter;

    @VisibleForTesting
    interface SurfaceAnimatorStarter {
        void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
                AnimationAdapter anim, boolean hidden, @AnimationType int type);
    }
    protected final WindowContainer mHost;

    Dimmer(WindowContainer host) {
        this(host, SurfaceAnimator::startAnimation);
    protected Dimmer(WindowContainer host) {
        mHost = host;
    }

    Dimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter) {
        mHost = host;
        mSurfaceAnimatorStarter = surfaceAnimatorStarter;
    // Constructs the correct type of dimmer
    static Dimmer create(WindowContainer host) {
        return Flags.dimmerRefactor() ? new SmoothDimmer(host) : new LegacyDimmer(host);
    }

    @NonNull
@@ -184,49 +48,8 @@ class Dimmer {
        return mHost;
    }

    private SurfaceControl makeDimLayer() {
        return mHost.makeChildSurface(null)
                .setParent(mHost.getSurfaceControl())
                .setColorLayer()
                .setName("Dim Layer for - " + mHost.getName())
                .setCallsite("Dimmer.makeDimLayer")
                .build();
    }

    /**
     * Retrieve the DimState, creating one if it doesn't exist.
     */
    private DimState getDimState(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 void dim(WindowContainer container, int relativeLayer, float alpha, int blurRadius) {
        final DimState d = getDimState(container);

        if (d == null) {
            return;
        }

        // The dim method is called from WindowState.prepareSurfaces(), which is always called
        // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
        // relative to the highest Z layer with a dim.
        SurfaceControl.Transaction t = mHost.getPendingTransaction();
        t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
        t.setAlpha(d.mDimLayer, alpha);
        t.setBackgroundBlurRadius(d.mDimLayer, blurRadius);

        d.mDimming = true;
    }
    protected abstract void dim(
            WindowContainer container, int relativeLayer, float alpha, int blurRadius);

    /**
     * Place a dim above the given container, which should be a child of the host container.
@@ -260,25 +83,15 @@ class Dimmer {
     * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
     * a chance to request dims to continue.
     */
    void resetDimStates() {
        if (mDimState == null) {
            return;
        }
        if (!mDimState.mDontReset) {
            mDimState.mDimming = false;
        }
    }
    abstract void resetDimStates();

    /** Returns non-null bounds if the dimmer is showing. */
    Rect getDimBounds() {
        return mDimState != null ? mDimState.mDimBounds : null;
    }
    abstract Rect getDimBounds();

    void dontAnimateExit() {
        if (mDimState != null) {
            mDimState.mAnimateExit = false;
        }
    }
    abstract void dontAnimateExit();

    @VisibleForTesting
    abstract SurfaceControl getDimLayer();

    /**
     * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
@@ -288,109 +101,5 @@ class Dimmer {
     * @param t      A transaction in which to update the dims.
     * @return true if any Dims were updated.
     */
    boolean updateDims(SurfaceControl.Transaction t) {
        if (mDimState == null) {
            return false;
        }

        if (!mDimState.mDimming) {
            if (!mDimState.mAnimateExit) {
                if (mDimState.mDimLayer.isValid()) {
                    t.remove(mDimState.mDimLayer);
                }
            } else {
                startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
            }
            mDimState = null;
            return false;
        } else {
            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());
            if (!mDimState.isVisible) {
                mDimState.isVisible = true;
                t.show(mDimState.mDimLayer);
                // Skip enter animation while starting window is on top of its activity
                final WindowState ws = mLastRequestedDimContainer.asWindowState();
                if (ws == null || ws.mActivityRecord == null
                        || ws.mActivityRecord.mStartingData == null) {
                    startDimEnter(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
                }
            }
            return true;
        }
    }

    private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
            SurfaceControl.Transaction t) {
        startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
    }

    private void startDimExit(WindowContainer container, SurfaceAnimator animator,
            SurfaceControl.Transaction t) {
        startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
    }

    private void startAnim(WindowContainer container, SurfaceAnimator animator,
            SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
        mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
                new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
                mHost.mWmService.mSurfaceAnimationRunner), false /* hidden */,
                ANIMATION_TYPE_DIMMER);
    }

    private long getDimDuration(WindowContainer container) {
        // If there's no container, then there isn't an animation occurring while dimming. Set the
        // duration to 0 so it immediately dims to the set alpha.
        if (container == null) {
            return 0;
        }

        // Otherwise 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 AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
        private final long mDuration;
        private final float mFromAlpha;
        private final float mToAlpha;

        AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
            mFromAlpha = fromAlpha;
            mToAlpha = toAlpha;
            mDuration = duration;
        }

        @Override
        public long getDuration() {
            return mDuration;
        }

        @Override
        public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
            final float fraction = getFraction(currentPlayTime);
            final float alpha = fraction * (mToAlpha - mFromAlpha) + mFromAlpha;
            t.setAlpha(sc, alpha);
        }

        @Override
        public void dump(PrintWriter pw, String prefix) {
            pw.print(prefix); pw.print("from="); pw.print(mFromAlpha);
            pw.print(" to="); pw.print(mToAlpha);
            pw.print(" duration="); pw.println(mDuration);
        }

        @Override
        public void dumpDebugInner(ProtoOutputStream proto) {
            final long token = proto.start(ALPHA);
            proto.write(FROM, mFromAlpha);
            proto.write(TO, mToAlpha);
            proto.write(DURATION_MS, mDuration);
            proto.end(token);
        }
    }
    abstract boolean updateDims(SurfaceControl.Transaction t);
}
Loading