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

Commit 2462a271 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Consider destruction of activity for task removal

... from organizer.

This aligns the behavior of display removal, that removes the
container until its children are destroyed without forcing.
In other words, the task requested to be removed will be
removed when the last activity belonging to the task reports
it has done the destruction. This also avoids the case that
the activity was removed from hierarchy but window process
controller still references to it, which will cause NPE in
ActivityRecord#cleanup.

Besides, adopt the hierarchical defer removal to task fragment
level (handleCompleteDeferredRemoval), so if it has something
animating, it will be removed until the animation is done.

Also
 - Fine tune some dump formats.
 - Fix wrong parent when removing child of non-root task.
 - Remove hasWindowsAlive because the window hierarchy was
   unified that app window belongs to activity directly.

Bug: 195770030
Test: TaskTests#testRemoveContainer
      ActivityRecordTests#testRemoveImmediately

Change-Id: Iddf0c4838103f36f40b8b4a7354ecd4c69a70200
parent c26e0802
Loading
Loading
Loading
Loading
+12 −30
Original line number Original line Diff line number Diff line
@@ -2444,20 +2444,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }
        }
    }
    }


    private void removeAppTokenFromDisplay() {
        if (mWmService.mRoot == null) return;

        final DisplayContent dc = mWmService.mRoot.getDisplayContent(getDisplayId());
        if (dc == null) {
            Slog.w(TAG, "removeAppTokenFromDisplay: Attempted to remove token: "
                    + appToken + " from non-existing displayId=" + getDisplayId());
            return;
        }
        // Resume key dispatching if it is currently paused before we remove the container.
        resumeKeyDispatchingLocked();
        dc.removeAppToken(appToken.asBinder());
    }

    /**
    /**
     * Reparents this activity into {@param newTaskFrag} at the provided {@param position}. The
     * Reparents this activity into {@param newTaskFrag} at the provided {@param position}. The
     * caller should ensure that the {@param newTaskFrag} is not already the parent of this
     * caller should ensure that the {@param newTaskFrag} is not already the parent of this
@@ -3008,7 +2994,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    @interface FinishRequest {}
    @interface FinishRequest {}


    /**
    /**
     * See {@link #finishIfPossible(int, Intent, String, boolean)}
     * See {@link #finishIfPossible(int, Intent, NeededUriGrants, String, boolean)}
     */
     */
    @FinishRequest int finishIfPossible(String reason, boolean oomAdj) {
    @FinishRequest int finishIfPossible(String reason, boolean oomAdj) {
        return finishIfPossible(Activity.RESULT_CANCELED,
        return finishIfPossible(Activity.RESULT_CANCELED,
@@ -3470,7 +3456,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        setState(DESTROYED, "removeFromHistory");
        setState(DESTROYED, "removeFromHistory");
        if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + this);
        if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + this);
        detachFromProcess();
        detachFromProcess();
        removeAppTokenFromDisplay();
        // Resume key dispatching if it is currently paused before we remove the container.
        resumeKeyDispatchingLocked();
        mDisplayContent.removeAppToken(appToken);


        cleanUpActivityServices();
        cleanUpActivityServices();
        removeUriPermissionsLocked();
        removeUriPermissionsLocked();
@@ -3687,12 +3675,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A


    @Override
    @Override
    void removeImmediately() {
    void removeImmediately() {
        if (!isState(DESTROYING, DESTROYED)) {
        if (mState != DESTROYED) {
            Slog.w(TAG, "Force remove immediately " + this + " state=" + mState);
            // If Task#removeImmediately is called directly with alive activities, ensure that the
            // If Task#removeImmediately is called directly with alive activities, ensure that the
            // activities are destroyed and detached from process.
            // activities are destroyed and detached from process.
            destroyImmediately("removeImmediately");
            destroyImmediately("removeImmediately");
        }
            // Complete the destruction immediately because this activity will not be found in
            // hierarchy, it is unable to report completion.
            destroyed("removeImmediately");
        } else {
            onRemovedFromDisplay();
            onRemovedFromDisplay();
        }
        super.removeImmediately();
        super.removeImmediately();
    }
    }


@@ -6624,17 +6617,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }
        }
    }
    }


    boolean hasWindowsAlive() {
        for (int i = mChildren.size() - 1; i >= 0; i--) {
            // No need to loop through child windows as the answer should be the same as that of the
            // parent window.
            if (!(mChildren.get(i)).mAppDied) {
                return true;
            }
        }
        return false;
    }

    void setWillReplaceWindows(boolean animate) {
    void setWillReplaceWindows(boolean animate) {
        ProtoLog.d(WM_DEBUG_ADD_REMOVE,
        ProtoLog.d(WM_DEBUG_ADD_REMOVE,
                "Marking app token %s with replacing windows.", this);
                "Marking app token %s with replacing windows.", this);
+11 −57
Original line number Original line Diff line number Diff line
@@ -125,7 +125,6 @@ import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
import static com.android.server.wm.TaskProto.TASK_FRAGMENT;
import static com.android.server.wm.TaskProto.TASK_FRAGMENT;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK;
import static com.android.server.wm.WindowContainerChildProto.TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
@@ -1513,7 +1512,7 @@ class Task extends TaskFragment {
        if (DEBUG_TASK_MOVEMENT) {
        if (DEBUG_TASK_MOVEMENT) {
            Slog.d(TAG_WM, "removeChild: child=" + r + " reason=" + reason);
            Slog.d(TAG_WM, "removeChild: child=" + r + " reason=" + reason);
        }
        }
        super.removeChild(r);
        super.removeChild(r, false /* removeSelfIfPossible */);


        if (inPinnedWindowingMode()) {
        if (inPinnedWindowingMode()) {
            // We normally notify listeners of task stack changes on pause, however root pinned task
            // We normally notify listeners of task stack changes on pause, however root pinned task
@@ -1543,7 +1542,10 @@ class Task extends TaskFragment {
            // Remove entire task if it doesn't have any activity left and it isn't marked for reuse
            // Remove entire task if it doesn't have any activity left and it isn't marked for reuse
            // or created by task organizer.
            // or created by task organizer.
            if (!isRootTask()) {
            if (!isRootTask()) {
                getRootTask().removeChild(this, reason);
                final WindowContainer<?> parent = getParent();
                if (parent != null) {
                    parent.asTaskFragment().removeChild(this);
                }
            }
            }
            EventLogTags.writeWmTaskRemoved(mTaskId,
            EventLogTags.writeWmTaskRemoved(mTaskId,
                    "removeChild:" + reason + " last r=" + r + " in t=" + this);
                    "removeChild:" + reason + " last r=" + r + " in t=" + this);
@@ -2594,25 +2596,12 @@ class Task extends TaskFragment {
        }
        }
    }
    }


    @VisibleForTesting
    boolean hasWindowsAlive() {
        return getActivity(ActivityRecord::hasWindowsAlive) != null;
    }

    @VisibleForTesting
    boolean shouldDeferRemoval() {
        if (mChildren.isEmpty()) {
            // No reason to defer removal of a Task that doesn't have any child.
            return false;
        }
        return hasWindowsAlive() && getRootTask().isAnimating(TRANSITION | CHILDREN);
    }

    @Override
    @Override
    void removeImmediately() {
    void removeImmediately() {
        removeImmediately("removeTask");
        removeImmediately("removeTask");
    }
    }


    @Override
    void removeImmediately(String reason) {
    void removeImmediately(String reason) {
        if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask:" + reason + " removing taskId=" + mTaskId);
        if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask:" + reason + " removing taskId=" + mTaskId);
        if (mRemoving) {
        if (mRemoving) {
@@ -3393,19 +3382,9 @@ class Task extends TaskFragment {
    @Override
    @Override
    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
        super.dump(pw, prefix, dumpAll);
        super.dump(pw, prefix, dumpAll);
        pw.println(prefix + "bounds=" + getBounds().toShortString());
        final String doublePrefix = prefix + "  ";
        for (int i = mChildren.size() - 1; i >= 0; i--) {
            final WindowContainer<?> child = mChildren.get(i);
            pw.println(prefix + "* " + child);
            // Only dump non-activity because full activity info is already printed by
            // RootWindowContainer#dumpActivities.
            if (child.asActivityRecord() == null) {
                child.dump(pw, doublePrefix, dumpAll);
            }
        }


        if (!mExitingActivities.isEmpty()) {
        if (!mExitingActivities.isEmpty()) {
            final String doublePrefix = prefix + "  ";
            pw.println();
            pw.println();
            pw.println(prefix + "Exiting application tokens:");
            pw.println(prefix + "Exiting application tokens:");
            for (int i = mExitingActivities.size() - 1; i >= 0; i--) {
            for (int i = mExitingActivities.size() - 1; i >= 0; i--) {
@@ -5358,18 +5337,6 @@ class Task extends TaskFragment {
        return true;
        return true;
    }
    }


    /** Finish all activities in the root task without waiting. */
    void finishAllActivitiesImmediately() {
        if (!hasChild()) {
            removeIfPossible("finishAllActivitiesImmediately");
            return;
        }
        forAllActivities((r) -> {
            Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
            r.destroyIfPossible("finishAllActivitiesImmediately");
        });
    }

    /** @return true if the root task behind this one is a standard activity type. */
    /** @return true if the root task behind this one is a standard activity type. */
    private boolean inFrontOfStandardRootTask() {
    private boolean inFrontOfStandardRootTask() {
        final TaskDisplayArea taskDisplayArea = getDisplayArea();
        final TaskDisplayArea taskDisplayArea = getDisplayArea();
@@ -5765,17 +5732,14 @@ class Task extends TaskFragment {


    @Override
    @Override
    void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
    void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
        pw.print(prefix); pw.print("* "); pw.println(this);
        super.dumpInner(prefix, pw, dumpAll, dumpPackage);
        pw.println(prefix + "  mBounds=" + getRequestedOverrideBounds());
        if (mCreatedByOrganizer) {
        pw.println(prefix + "  mCreatedByOrganizer=" + mCreatedByOrganizer);
            pw.println(prefix + "  mCreatedByOrganizer=true");
        }
        if (mLastNonFullscreenBounds != null) {
        if (mLastNonFullscreenBounds != null) {
            pw.print(prefix); pw.print("  mLastNonFullscreenBounds=");
            pw.print(prefix); pw.print("  mLastNonFullscreenBounds=");
            pw.println(mLastNonFullscreenBounds);
            pw.println(mLastNonFullscreenBounds);
        }
        }
        if (dumpAll) {
            printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
                    prefix + "  mLastPausedActivity: ", null);
        }
        if (isLeafTask()) {
        if (isLeafTask()) {
            pw.println(prefix + "  isSleeping=" + shouldSleepActivities());
            pw.println(prefix + "  isSleeping=" + shouldSleepActivities());
            printThisActivity(pw, getTopPausingActivity(), dumpPackage, false,
            printThisActivity(pw, getTopPausingActivity(), dumpPackage, false,
@@ -6172,16 +6136,6 @@ class Task extends TaskFragment {
        getDisplayContent().getPinnedTaskController().setActions(actions);
        getDisplayContent().getPinnedTaskController().setActions(actions);
    }
    }


    /** Returns true if a removal action is still being deferred. */
    boolean handleCompleteDeferredRemoval() {
        if (isAnimating(TRANSITION | CHILDREN)
                || mAtmService.getTransitionController().inTransition(this)) {
            return true;
        }

        return super.handleCompleteDeferredRemoval();
    }

    public DisplayInfo getDisplayInfo() {
    public DisplayInfo getDisplayInfo() {
        return mDisplayContent.getDisplayInfo();
        return mDisplayContent.getDisplayInfo();
    }
    }
+1 −1
Original line number Original line Diff line number Diff line
@@ -2104,7 +2104,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
            if (destroyContentOnRemoval
            if (destroyContentOnRemoval
                    || !task.isActivityTypeStandardOrUndefined()
                    || !task.isActivityTypeStandardOrUndefined()
                    || task.mCreatedByOrganizer) {
                    || task.mCreatedByOrganizer) {
                task.finishAllActivitiesImmediately();
                task.remove(false /* withTransition */, "removeTaskDisplayArea");
            } else {
            } else {
                // Reparent task to corresponding launch root or display area.
                // Reparent task to corresponding launch root or display area.
                final WindowContainer launchRoot =
                final WindowContainer launchRoot =
+85 −9
Original line number Original line Diff line number Diff line
@@ -61,6 +61,8 @@ import static com.android.server.wm.TaskFragmentProto.DISPLAY_ID;
import static com.android.server.wm.TaskFragmentProto.MIN_HEIGHT;
import static com.android.server.wm.TaskFragmentProto.MIN_HEIGHT;
import static com.android.server.wm.TaskFragmentProto.MIN_WIDTH;
import static com.android.server.wm.TaskFragmentProto.MIN_WIDTH;
import static com.android.server.wm.TaskFragmentProto.WINDOW_CONTAINER;
import static com.android.server.wm.TaskFragmentProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK_FRAGMENT;
import static com.android.server.wm.WindowContainerChildProto.TASK_FRAGMENT;


import android.annotation.IntDef;
import android.annotation.IntDef;
@@ -158,8 +160,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
     */
     */
    int mMinHeight;
    int mMinHeight;


    /** Avoid reentrant of {@link #removeImmediately()}. */
    /** This task fragment will be removed when the cleanup of its children are done. */
    private boolean mRemoving;
    private boolean mIsRemovalRequested;


    /** The TaskFragment that is adjacent to this one. */
    /** The TaskFragment that is adjacent to this one. */
    @Nullable
    @Nullable
@@ -2047,15 +2049,67 @@ class TaskFragment extends WindowContainer<WindowContainer> {
    }
    }


    @Override
    @Override
    void removeImmediately() {
    void removeChild(WindowContainer child) {
        if (mRemoving) {
        removeChild(child, true /* removeSelfIfPossible */);
    }

    void removeChild(WindowContainer child, boolean removeSelfIfPossible) {
        super.removeChild(child);
        if (removeSelfIfPossible && (!mCreatedByOrganizer || mIsRemovalRequested) && !hasChild()) {
            removeImmediately("removeLastChild " + child);
        }
    }

    /**
     * Requests to remove this task fragment. If it doesn't have children, it is removed
     * immediately. Otherwise it will be removed until all activities are destroyed.
     *
     * @param withTransition Whether to use transition animation when removing activities. Set to
     *                       {@code false} if this is invisible to user, e.g. display removal.
     */
    void remove(boolean withTransition, String reason) {
        if (!hasChild()) {
            removeImmediately(reason);
            return;
            return;
        }
        }
        mRemoving = true;
        mIsRemovalRequested = true;
        forAllActivities(r -> {
            if (withTransition) {
                r.finishIfPossible(reason, false /* oomAdj */);
            } else {
                r.destroyIfPossible(reason);
            }
        });
    }

    boolean shouldDeferRemoval() {
        if (!hasChild()) {
            return false;
        }
        return isAnimating(TRANSITION | CHILDREN, WindowState.EXIT_ANIMATING_TYPES)
                || mAtmService.getTransitionController().inTransition(this);
    }

    @Override
    boolean handleCompleteDeferredRemoval() {
        if (shouldDeferRemoval()) {
            return true;
        }
        return super.handleCompleteDeferredRemoval();
    }

    /** The overridden method must call {@link #removeImmediately()} instead of super. */
    void removeImmediately(String reason) {
        Slog.d(TAG, "Remove task fragment: " + reason);
        removeImmediately();
    }

    @Override
    void removeImmediately() {
        mIsRemovalRequested = false;
        resetAdjacentTaskFragment();
        resetAdjacentTaskFragment();
        super.removeImmediately();
        super.removeImmediately();
        sendTaskFragmentVanished();
        sendTaskFragmentVanished();
        mRemoving = false;
    }
    }


    boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll,
    boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll,
@@ -2097,13 +2151,35 @@ class TaskFragment extends WindowContainer<WindowContainer> {


    void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
    void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
        pw.print(prefix); pw.print("* "); pw.println(this);
        pw.print(prefix); pw.print("* "); pw.println(this);
        pw.println(prefix + "  mBounds=" + getRequestedOverrideBounds());
        final Rect bounds = getRequestedOverrideBounds();
        if (!bounds.isEmpty()) {
            pw.println(prefix + "  mBounds=" + bounds);
        }
        if (mIsRemovalRequested) {
            pw.println(prefix + "  mIsRemovalRequested=true");
        }
        if (dumpAll) {
        if (dumpAll) {
            printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
            printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
                    prefix + "  mLastPausedActivity: ", null);
                    prefix + "  mLastPausedActivity: ", null);
        }
        }
    }
    }


    @Override
    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
        super.dump(pw, prefix, dumpAll);
        pw.println(prefix + "bounds=" + getBounds().toShortString());
        final String doublePrefix = prefix + "  ";
        for (int i = mChildren.size() - 1; i >= 0; i--) {
            final WindowContainer<?> child = mChildren.get(i);
            pw.println(prefix + "* " + child);
            // Only dump non-activity because full activity info is already printed by
            // RootWindowContainer#dumpActivities.
            if (child.asActivityRecord() == null) {
                child.dump(pw, doublePrefix, dumpAll);
            }
        }
    }

    @Override
    @Override
    void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
    void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
        final long token = proto.start(fieldId);
        final long token = proto.start(fieldId);
+10 −5
Original line number Original line Diff line number Diff line
@@ -230,12 +230,17 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
        validateAndGetState(organizer);
        validateAndGetState(organizer);
        final int pid = Binder.getCallingPid();
        final int pid = Binder.getCallingPid();
        final long uid = Binder.getCallingUid();
        final long uid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
            synchronized (mGlobalLock) {
                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
                        "Unregister task fragment organizer=%s uid=%d pid=%d",
                        "Unregister task fragment organizer=%s uid=%d pid=%d",
                        organizer.asBinder(), uid, pid);
                        organizer.asBinder(), uid, pid);
                removeOrganizer(organizer);
                removeOrganizer(organizer);
            }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
    }


    void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
    void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
Loading