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

Commit 773050d2 authored by Tracy Zhou's avatar Tracy Zhou
Browse files

Fix a memory leak

Currently we pass a callback from AbsSwipeUpHandler to TaskAnimationManager to render task view launch animation, exposing the launcher activity to the static TouchInteractionService, which causes the leak. To not only fix the leak, but consider a better place for this functionality semantically, RecentsView is a better home.

Fixes: 183458638
Test: Run presubmit

Change-Id: Id35d0ad22005003d2516c34515a5cb338605be0b
parent 6914f93f
Loading
Loading
Loading
Loading
+7 −101
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import static android.widget.Toast.LENGTH_SHORT;

import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
@@ -97,8 +96,8 @@ import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.InputConsumerProxy;
import com.android.quickstep.util.InputProxyHandlerFactory;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.RectFSpringAnim;
@@ -113,12 +112,10 @@ import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.TaskInfoCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Consumer;

/**
@@ -255,7 +252,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
        mActivityInterface = gestureState.getActivityInterface();
        mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
        mInputConsumerProxy =
                new InputConsumerProxy(inputConsumer, this::createNewInputProxyHandler);
                new InputConsumerProxy(inputConsumer, () -> {
                    endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
                    endLauncherTransitionController();
                }, new InputProxyHandlerFactory(mActivityInterface, mGestureState));
        mTaskAnimationManager = taskAnimationManager;
        mTouchTimeMs = touchTimeMs;
        mContinuingLastGesture = continuingLastGesture;
@@ -783,19 +783,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
        handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */);
    }

    /**
     * Called to create a input proxy for the running task
     */
    @UiThread
    protected InputConsumer createNewInputProxyHandler() {
        endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
        endLauncherTransitionController();

        StatefulActivity activity = mActivityInterface.getCreatedActivity();
        return activity == null ? InputConsumer.NO_OP
                : new OverviewInputConsumer(mGestureState, activity, null, true);
    }

    private void endRunningWindowAnim(boolean cancel) {
        if (mRunningWindowAnim != null) {
            if (cancel) {
@@ -1338,6 +1325,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
            mInputConsumerProxy.destroy();
            mTaskAnimationManager.setLiveTileCleanUpHandler(null);
        }
        mInputConsumerProxy.unregisterCallback();
        endRunningWindowAnim(false /* cancel */);

        if (mGestureEndCallback != null) {
@@ -1502,36 +1490,13 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,

    protected abstract void finishRecentsControllerToHome(Runnable callback);

    private final TaskStackChangeListener mLiveTileRestartListener = new TaskStackChangeListener() {
        @Override
        public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
                boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
            if (mRecentsView.getRunningTaskIndex() != -1
                    && mRecentsView.getRunningTaskId() == task.taskId
                    && mRecentsAnimationTargets.hasTask(task.taskId)) {
                launchOtherTaskInLiveTileMode(task.taskId, mRecentsAnimationTargets.apps);
            }
            ActivityManagerWrapper.getInstance().unregisterTaskStackListener(
                    mLiveTileRestartListener);
        }
    };

    private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
        endLauncherTransitionController();
        mActivityInterface.onSwipeUpToRecentsComplete();
        mRecentsView.onSwipeUpAnimationSuccess();
        if (LIVE_TILE.get()) {
            mTaskAnimationManager.setLaunchOtherTaskInLiveTileModeHandler(
                    appearedTaskTarget -> {
                        RemoteAnimationTargetCompat[] apps = Arrays.copyOf(
                                mRecentsAnimationTargets.apps,
                                mRecentsAnimationTargets.apps.length + 1);
                        apps[apps.length - 1] = appearedTaskTarget;
                        launchOtherTaskInLiveTileMode(appearedTaskTarget.taskId, apps);
                    });
            mTaskAnimationManager.setLiveTileCleanUpHandler(mInputConsumerProxy::destroy);
            ActivityManagerWrapper.getInstance().registerTaskStackListener(
                    mLiveTileRestartListener);
            mTaskAnimationManager.enableLiveTileRestartListener();
        }

        SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
@@ -1539,65 +1504,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
        reset();
    }

    private void launchOtherTaskInLiveTileMode(int taskId, RemoteAnimationTargetCompat[] apps) {
        AnimatorSet anim = new AnimatorSet();
        TaskView taskView = mRecentsView.getTaskView(taskId);
        if (taskView == null || !mRecentsView.isTaskViewVisible(taskView)) {
            // TODO: Refine this animation.
            SurfaceTransactionApplier surfaceApplier =
                    new SurfaceTransactionApplier(mActivity.getDragLayer());
            ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
            appAnimator.setDuration(RECENTS_LAUNCH_DURATION);
            appAnimator.setInterpolator(ACCEL_DEACCEL);
            appAnimator.addUpdateListener(new MultiValueUpdateListener() {
                @Override
                public void onUpdate(float percent) {
                    SurfaceParams.Builder builder = new SurfaceParams.Builder(
                            apps[apps.length - 1].leash);
                    Matrix matrix = new Matrix();
                    matrix.postScale(percent, percent);
                    matrix.postTranslate(mDp.widthPx * (1 - percent) / 2,
                            mDp.heightPx * (1 - percent) / 2);
                    builder.withAlpha(percent).withMatrix(matrix);
                    surfaceApplier.scheduleApply(builder.build());
                }
            });
            anim.play(appAnimator);
        } else {
            TaskViewUtils.composeRecentsLaunchAnimator(
                    anim, taskView, apps,
                    mRecentsAnimationTargets.wallpapers, true /* launcherClosing */,
                    mActivity.getStateManager(), mRecentsView,
                    mActivityInterface.getDepthController());
        }
        anim.addListener(new AnimatorListenerAdapter(){

            @Override
            public void onAnimationEnd(Animator animator) {
                cleanUp(false);
            }

            @Override
            public void onAnimationCancel(Animator animator) {
                cleanUp(true);
            }

            private void cleanUp(boolean canceled) {
                if (mRecentsAnimationController != null) {
                    mRecentsAnimationController.finish(false /* toRecents */,
                            null /* onFinishComplete */);
                    if (canceled) {
                        mRecentsAnimationController = null;
                    } else {
                        mActivityInterface.onLaunchTaskSuccess();
                    }
                    ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
                }
            }
        });
        anim.start();
    }

    private static boolean isNotInRecents(RemoteAnimationTargetCompat app) {
        return app.isNotInRecents
                || app.activityType == ACTIVITY_TYPE_HOME;
+0 −9
Original line number Diff line number Diff line
@@ -41,13 +41,4 @@ public class RecentsAnimationTargets extends RemoteAnimationTargets {
    public boolean hasTargets() {
        return unfilteredApps.length != 0;
    }

    public boolean hasTask(int taskId) {
        for (RemoteAnimationTargetCompat target : unfilteredApps) {
            if (target.taskId == taskId) {
                return true;
            }
        }
        return false;
    }
}
+32 −17
Original line number Diff line number Diff line
@@ -17,10 +17,11 @@ package com.android.quickstep;

import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;

import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -31,13 +32,13 @@ import androidx.annotation.UiThread;

import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.RemoteTransitionCompat;

import java.util.function.Consumer;
import com.android.systemui.shared.system.TaskStackChangeListener;

public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
    public static final boolean ENABLE_SHELL_TRANSITIONS =
@@ -49,10 +50,24 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
    // Temporary until we can hook into gesture state events
    private GestureState mLastGestureState;
    private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
    private Consumer<RemoteAnimationTargetCompat> mLaunchOtherTaskHandler;
    private Runnable mLiveTileCleanUpHandler;
    private Context mCtx;

    private final TaskStackChangeListener mLiveTileRestartListener = new TaskStackChangeListener() {
        @Override
        public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
                boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
            BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
            if (LIVE_TILE.get() && activityInterface.isInLiveTileMode()
                    && activityInterface.getCreatedActivity() != null) {
                RecentsView recentsView = activityInterface.getCreatedActivity().getOverviewPanel();
                recentsView.launchSideTaskInLiveTileModeForRestartedApp(task.taskId);
                ActivityManagerWrapper.getInstance().unregisterTaskStackListener(
                        mLiveTileRestartListener);
            }
        }
    };

    TaskAnimationManager(Context ctx) {
        mCtx = ctx;
    }
@@ -114,9 +129,14 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn

            @Override
            public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
                if (mLaunchOtherTaskHandler != null
                        && mLastGestureState.getEndTarget() == RECENTS) {
                    mLaunchOtherTaskHandler.accept(appearedTaskTarget);
                BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
                if (LIVE_TILE.get() && activityInterface.isInLiveTileMode()
                        && activityInterface.getCreatedActivity() != null) {
                    RecentsView recentsView =
                            activityInterface.getCreatedActivity().getOverviewPanel();
                    RemoteAnimationTargetCompat[] apps = new RemoteAnimationTargetCompat[1];
                    apps[0] = appearedTaskTarget;
                    recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId, apps);
                    return;
                }
                if (mController != null) {
@@ -161,17 +181,12 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
        return mCallbacks;
    }

    /**
     * The passed-in handler is used to render side task launch animation in recents in live tile
     * mode.
     */
    public void setLaunchOtherTaskInLiveTileModeHandler(
            Consumer<RemoteAnimationTargetCompat> handler) {
        mLaunchOtherTaskHandler = handler;
    public void setLiveTileCleanUpHandler(Runnable cleanUpHandler) {
        mLiveTileCleanUpHandler = cleanUpHandler;
    }

    public void setLiveTileCleanUpHandler(Runnable runnable) {
        mLiveTileCleanUpHandler = runnable;
    public void enableLiveTileRestartListener() {
        ActivityManagerWrapper.getInstance().registerTaskStackListener(mLiveTileRestartListener);
    }

    /**
@@ -215,6 +230,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
            mLiveTileCleanUpHandler.run();
            mLiveTileCleanUpHandler = null;
        }
        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mLiveTileRestartListener);

        // Release all the target leashes
        if (mTargets != null) {
@@ -231,7 +247,6 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
        mTargets = null;
        mLastGestureState = null;
        mLastAppearedTaskTarget = null;
        mLaunchOtherTaskHandler = null;
    }

    public void dump() {
+20 −8
Original line number Diff line number Diff line
@@ -38,7 +38,8 @@ public class InputConsumerProxy {
    private static final String TAG = "InputConsumerProxy";

    private final InputConsumerController mInputConsumerController;
    private final Supplier<InputConsumer> mConsumerSupplier;
    private Runnable mCallback;
    private Supplier<InputConsumer> mConsumerSupplier;

    // The consumer is created lazily on demand.
    private InputConsumer mInputConsumer;
@@ -48,8 +49,9 @@ public class InputConsumerProxy {
    private boolean mDestroyPending = false;

    public InputConsumerProxy(InputConsumerController inputConsumerController,
            Supplier<InputConsumer> consumerSupplier) {
            Runnable callback, Supplier<InputConsumer> consumerSupplier) {
        mInputConsumerController = inputConsumerController;
        mCallback = callback;
        mConsumerSupplier = consumerSupplier;
    }

@@ -64,9 +66,7 @@ public class InputConsumerProxy {
        if (ev instanceof MotionEvent) {
            onInputConsumerMotionEvent((MotionEvent) ev);
        } else if (ev instanceof KeyEvent) {
            if (mInputConsumer == null) {
                mInputConsumer = mConsumerSupplier.get();
            }
            initInputConsumerIfNeeded();
            mInputConsumer.onKeyEvent((KeyEvent) ev);
            return true;
        }
@@ -89,9 +89,7 @@ public class InputConsumerProxy {

        if (action == ACTION_DOWN) {
            mTouchInProgress = true;
            if (mInputConsumer == null) {
                mInputConsumer = mConsumerSupplier.get();
            }
            initInputConsumerIfNeeded();
        } else if (action == ACTION_CANCEL || action == ACTION_UP) {
            // Finish any pending actions
            mTouchInProgress = false;
@@ -115,4 +113,18 @@ public class InputConsumerProxy {
        mDestroyed = true;
        mInputConsumerController.setInputListener(null);
    }

    public void unregisterCallback() {
        mCallback = null;
    }

    private void initInputConsumerIfNeeded() {
        if (mInputConsumer == null) {
            if (mCallback != null) {
                mCallback.run();
            }
            mInputConsumer = mConsumerSupplier.get();
            mConsumerSupplier = null;
        }
    }
}
+53 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.quickstep.util;

import androidx.annotation.UiThread;

import com.android.launcher3.statemanager.StatefulActivity;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;

import java.util.function.Supplier;

/**
 * A factory that creates a input consumer for
 *  {@link com.android.quickstep.util.InputConsumerProxy}.
 */
public class InputProxyHandlerFactory implements Supplier<InputConsumer> {

    private final BaseActivityInterface mActivityInterface;
    private final GestureState mGestureState;

    @UiThread
    public InputProxyHandlerFactory(BaseActivityInterface activityInterface,
            GestureState gestureState) {
        mActivityInterface = activityInterface;
        mGestureState = gestureState;
    }

    /**
     * Called to create a input proxy for the running task
     */
    @Override
    public InputConsumer get() {
        StatefulActivity activity = mActivityInterface.getCreatedActivity();
        return activity == null ? InputConsumer.NO_OP
                : new OverviewInputConsumer(mGestureState, activity, null, true);
    }
}
Loading