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

Commit 8692eff3 authored by Louis Chang's avatar Louis Chang
Browse files

Adding new APIs to save state and retrieve it

The TaskFragmentOrganizer now can save state in the system, which
could be retrieved later if the TaskFragmentOrganizer was killed
by the system (or app just randomly crashed) and re-registered again
once the process restarted.

Bug: 289875940
Test: wm presubmit
Flag: com.android.window.flags.ae_back_stack_restore
Change-Id: I14942b70121133c2031acfa143eddfc51e9bffa5
parent cca31ca5
Loading
Loading
Loading
Loading
+16 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.window;

import android.os.Bundle;
import android.os.IBinder;
import android.view.RemoteAnimationDefinition;
import android.window.ITaskFragmentOrganizer;
@@ -24,20 +25,33 @@ import android.window.WindowContainerTransaction;

/** @hide */
interface ITaskFragmentOrganizerController {

    /**
     * Registers a TaskFragmentOrganizer to manage TaskFragments. Registering a system
     * organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer will have additional
     * system capabilities.
     *
     * @param organizer          The TaskFragmentOrganizer to register
     * @param isSystemOrganizer  If it is a system organizer
     * @param outSavedState      Returning the saved state (if any) that previously saved. This is
     *                           useful when retrieve the state from the same TaskFragmentOrganizer
     *                           that was killed by the system (e.g. to reclaim memory). Note that
     *                           the save state is dropped and unable to retrieve once the system
     *                           restarts or the organizer is unregistered.
     */
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true)")
    void registerOrganizer(in ITaskFragmentOrganizer organizer, in boolean isSystemOrganizer);
    void registerOrganizer(in ITaskFragmentOrganizer organizer, in boolean isSystemOrganizer, out Bundle outSavedState);

    /**
     * Unregisters a previously registered TaskFragmentOrganizer.
     */
    void unregisterOrganizer(in ITaskFragmentOrganizer organizer);

    /**
     * Saves the state in the system, where the state can be restored if the process of
     * the TaskFragmentOrganizer is restarted.
     */
    void setSavedState(in ITaskFragmentOrganizer organizer, in Bundle savedState);

    /**
     * Notifies the server that the organizer has finished handling the given transaction. The
     * server should apply the given {@link WindowContainerTransaction} for the necessary changes.
+50 −8
Original line number Diff line number Diff line
@@ -165,17 +165,12 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
     */
    @CallSuper
    public void registerOrganizer() {
        // TODO(b/302420256) point to registerOrganizer(boolean) when flag is removed.
        try {
            getController().registerOrganizer(mInterface, false /* isSystemOrganizer */);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        registerOrganizer(false /* isSystemOrganizer */, null /* outSavedState */);
    }

    /**
     * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments.
     *
     * <p>
     * Registering a system organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer
     * will have additional system capabilities, including: (1) it will receive SurfaceControl for
     * the organized TaskFragment, and (2) it needs to update the
@@ -187,8 +182,31 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
    @RequiresPermission(value = "android.permission.MANAGE_ACTIVITY_TASKS", conditional = true)
    @FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG)
    public void registerOrganizer(boolean isSystemOrganizer) {
        registerOrganizer(isSystemOrganizer, null /* outSavedState */);
    }

    /**
     * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments.
     * <p>
     * Registering a system organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer
     * will have additional system capabilities, including: (1) it will receive SurfaceControl for
     * the organized TaskFragment, and (2) it needs to update the
     * {@link android.view.SurfaceControl} following the window change accordingly.
     *
     * @param isSystemOrganizer  If it is a system organizer
     * @param outSavedState      Returning the saved state (if any) that previously saved. This is
     *                           useful when retrieve the state from the same TaskFragmentOrganizer
     *                           that was killed by the system (e.g. to reclaim memory). Note that
     *                           the save state is dropped and unable to retrieve once the system
     *                           restarts or the organizer is unregistered.
     * @hide
     */
    @CallSuper
    @RequiresPermission(value = "android.permission.MANAGE_ACTIVITY_TASKS", conditional = true)
    public void registerOrganizer(boolean isSystemOrganizer, @Nullable Bundle outSavedState) {
        try {
            getController().registerOrganizer(mInterface, isSystemOrganizer);
            getController().registerOrganizer(mInterface, isSystemOrganizer,
                    outSavedState != null ? outSavedState : new Bundle());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -206,6 +224,30 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
        }
    }

    /**
     * Saves the state in the system, where the state can be restored if the process of
     * the TaskFragmentOrganizer is restarted.
     *
     * @hide
     *
     * @param state the state to save.
     */
    public void setSavedState(@NonNull Bundle state) {
        if (!Flags.aeBackStackRestore()) {
            return;
        }

        if (state.getSize() > 200000) {
            throw new IllegalArgumentException("Saved state too large, " + state.getSize());
        }

        try {
            getController().setSavedState(mInterface, state);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Notifies the server that the organizer has finished handling the given transaction. The
     * server should apply the given {@link WindowContainerTransaction} for the necessary changes.
+11 −1
Original line number Diff line number Diff line
@@ -478,6 +478,10 @@ class TaskFragment extends WindowContainer<WindowContainer> {
        mTaskFragmentOrganizerProcessName = processName;
    }

    void onTaskFragmentOrganizerRestarted(@NonNull ITaskFragmentOrganizer organizer) {
        mTaskFragmentOrganizer = organizer;
    }

    void onTaskFragmentOrganizerRemoved() {
        mTaskFragmentOrganizer = null;
    }
@@ -2960,7 +2964,13 @@ class TaskFragment extends WindowContainer<WindowContainer> {
    }

    boolean shouldRemoveSelfOnLastChildRemoval() {
        return !mCreatedByOrganizer || mIsRemovalRequested;
        if (!mCreatedByOrganizer || mIsRemovalRequested) {
            return true;
        }
        // The TaskFragmentOrganizer may be killed while the embedded TaskFragments remains in WM
        // core. The embedded TaskFragment should still be removed when the child activities are
        // all gone (like package force-stopped).
        return mIsEmbedded && mTaskFragmentOrganizer == null;
    }

    /**
+100 −13
Original line number Diff line number Diff line
@@ -85,6 +85,12 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
     */
    private final ArrayMap<IBinder, TaskFragmentOrganizerState> mTaskFragmentOrganizerState =
            new ArrayMap<>();
    /**
     * The cached {@link TaskFragmentOrganizerState} for the {@link ITaskFragmentOrganizer} that
     * have no running process.
     */
    private final ArrayList<TaskFragmentOrganizerState> mCachedTaskFragmentOrganizerStates =
            new ArrayList<>();
    /**
     * Map from {@link ITaskFragmentOrganizer} to a list of related {@link PendingTaskFragmentEvent}
     */
@@ -105,11 +111,16 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
     * {@link TaskFragment TaskFragments}.
     */
    private class TaskFragmentOrganizerState implements IBinder.DeathRecipient {
        @NonNull
        private final ArrayList<TaskFragment> mOrganizedTaskFragments = new ArrayList<>();
        private final IApplicationThread mAppThread;
        private final ITaskFragmentOrganizer mOrganizer;
        private final int mOrganizerPid;
        private final int mOrganizerUid;
        @NonNull
        private IApplicationThread mAppThread;
        @NonNull
        private ITaskFragmentOrganizer mOrganizer;
        private int mOrganizerPid;
        @Nullable
        private Bundle mSavedState;

        /**
         * Map from {@link TaskFragment} to the last {@link TaskFragmentInfo} sent to the
@@ -182,6 +193,24 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
            }
        }

        void restore(@NonNull ITaskFragmentOrganizer organizer, int pid) {
            mOrganizer = organizer;
            mOrganizerPid = pid;
            mAppThread = getAppThread(pid, mOrganizerUid);
            for (int i = mOrganizedTaskFragments.size() - 1; i >= 0; i--) {
                mOrganizedTaskFragments.get(i).onTaskFragmentOrganizerRestarted(organizer);
            }
            try {
                mOrganizer.asBinder().linkToDeath(this, 0 /*flags*/);
            } catch (RemoteException e) {
                Slog.e(TAG, "TaskFragmentOrganizer failed to register death recipient");
            }
        }

        boolean hasSavedState() {
            return mSavedState != null && !mSavedState.isEmpty();
        }

        /**
         * @return {@code true} if taskFragment is organized and not sent the appeared event before.
         */
@@ -226,13 +255,19 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
                        containsNonEmbeddedActivity ? null : task,
                        null /* remoteTransition */, null /* displayChange */);
            }

            // Defer to avoid unnecessary layout when there are multiple TaskFragments removal.
            mAtmService.deferWindowLayout();
            try {
                while (!mOrganizedTaskFragments.isEmpty()) {
                    final TaskFragment taskFragment = mOrganizedTaskFragments.remove(0);
                // Removes the TaskFragments if no saved state or is empty.
                final boolean haveSavedState = hasSavedState();
                for (int i = mOrganizedTaskFragments.size() - 1; i >= 0; i--) {
                    final TaskFragment taskFragment = mOrganizedTaskFragments.get(i);
                    if (!haveSavedState || !taskFragment.hasChild()) {
                        mOrganizedTaskFragments.remove(i);
                        taskFragment.removeImmediately();
                    }
                }
            } finally {
                mAtmService.continueWindowLayout();
            }
@@ -478,15 +513,15 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
    }

    @Override
    public void registerOrganizer(
            @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
        registerOrganizerInternal(
                organizer,
                Flags.taskFragmentSystemOrganizerFlag() && isSystemOrganizer);
    public void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer,
            boolean isSystemOrganizer, @NonNull Bundle outSavedState) {
        registerOrganizerInternal(organizer,
                Flags.taskFragmentSystemOrganizerFlag() && isSystemOrganizer, outSavedState);
    }

    private void registerOrganizerInternal(
            @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
            @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer,
            @NonNull Bundle outSavedState) {
        if (isSystemOrganizer) {
            enforceTaskPermission("registerSystemOrganizer()");
        }
@@ -496,16 +531,49 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
                    "Register task fragment organizer=%s uid=%d pid=%d",
                    organizer.asBinder(), uid, pid);

            if (isOrganizerRegistered(organizer)) {
                throw new IllegalStateException(
                        "Replacing existing organizer currently unsupported");
            }

            if (restoreFromCachedStateIfPossible(organizer, pid, uid, outSavedState)) {
                return;
            }

            mTaskFragmentOrganizerState.put(organizer.asBinder(),
                    new TaskFragmentOrganizerState(organizer, pid, uid, isSystemOrganizer));
            mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
        }
    }

    private boolean restoreFromCachedStateIfPossible(@NonNull ITaskFragmentOrganizer organizer,
            int pid, int uid, @NonNull Bundle outSavedState) {
        if (!Flags.aeBackStackRestore()) {
            return false;
        }

        TaskFragmentOrganizerState cachedState = null;
        int i = mCachedTaskFragmentOrganizerStates.size() - 1;
        for (; i >= 0; i--) {
            final TaskFragmentOrganizerState state = mCachedTaskFragmentOrganizerStates.get(i);
            if (state.mOrganizerUid == uid) {
                cachedState = state;
                break;
            }
        }
        if (cachedState == null) {
            return false;
        }

        mCachedTaskFragmentOrganizerStates.remove(cachedState);
        outSavedState.putAll(cachedState.mSavedState);
        cachedState.restore(organizer, pid);
        mTaskFragmentOrganizerState.put(organizer.asBinder(), cachedState);
        mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
        return true;
    }

    @Override
    public void unregisterOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
        final int pid = Binder.getCallingPid();
@@ -523,6 +591,23 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
        }
    }

    @Override
    public void setSavedState(@NonNull ITaskFragmentOrganizer organizer, @Nullable Bundle state) {
        final int pid = Binder.getCallingPid();
        final int uid = Binder.getCallingUid();
        synchronized (mGlobalLock) {
            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set state for organizer=%s uid=%d pid=%d",
                    organizer.asBinder(), uid, pid);
            final TaskFragmentOrganizerState organizerState = mTaskFragmentOrganizerState.get(
                    organizer.asBinder());
            if (organizerState == null) {
                throw new IllegalStateException("The organizer hasn't been registered.");
            }

            organizerState.mSavedState = state;
        }
    }

    @Override
    public void onTransactionHandled(@NonNull IBinder transactionToken,
            @NonNull WindowContainerTransaction wct,
@@ -768,9 +853,11 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
        // Remove any pending event of this organizer first because state.dispose() may trigger
        // event dispatch as result of surface placement.
        mPendingTaskFragmentEvents.remove(organizer.asBinder());
        // remove all of the children of the organized TaskFragment
        state.dispose(reason);
        mTaskFragmentOrganizerState.remove(organizer.asBinder());
        if (state.hasSavedState()) {
            mCachedTaskFragmentOrganizerStates.add(state);
        }
    }

    /**
+2 −1
Original line number Diff line number Diff line
@@ -924,7 +924,8 @@ public class WindowTestsBase extends SystemServiceTestsBase {
            mSystemServicesTestRule.addProcess("pkgName", "procName",
                    WindowManagerService.MY_PID, WindowManagerService.MY_UID);
        }
        mAtm.mTaskFragmentOrganizerController.registerOrganizer(organizer, isSystemOrganizer);
        mAtm.mTaskFragmentOrganizerController.registerOrganizer(organizer, isSystemOrganizer,
                new Bundle());
    }

    /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */