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

Commit 959e0337 authored by Louis Chang's avatar Louis Chang
Browse files

Reconstruct the containers from the saved state

This needs to be done after the embedding rules are set, because
the SplitContainers are unable to be completely restored before
knowing the SplitRule.

Also gets and updates the latest TFInfo and TFParentInfo from the
system.

Bug: 289875940
Test: wm presubmit
Flag: com.android.window.flags.ae_back_stack_restore
Change-Id: Iaac580e0512939b0131dfa39c2588723360651ae
parent 2e8fb324
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -68,6 +68,23 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
     */
    public static final String KEY_ERROR_CALLBACK_OP_TYPE = "operation_type";

    /**
     * Key to bundle {@link TaskFragmentInfo}s from the system in
     * {@link #registerOrganizer(boolean, Bundle)}
     *
     * @hide
     */
    public static final String KEY_RESTORE_TASK_FRAGMENTS_INFO = "key_restore_task_fragments_info";

    /**
     * Key to bundle {@link TaskFragmentParentInfo} from the system in
     * {@link #registerOrganizer(boolean, Bundle)}
     *
     * @hide
     */
    public static final String KEY_RESTORE_TASK_FRAGMENT_PARENT_INFO =
            "key_restore_task_fragment_parent_info";

    /**
     * No change set.
     */
+110 −11
Original line number Diff line number Diff line
@@ -16,16 +16,26 @@

package androidx.window.extensions.embedding;

import static android.window.TaskFragmentOrganizer.KEY_RESTORE_TASK_FRAGMENTS_INFO;
import static android.window.TaskFragmentOrganizer.KEY_RESTORE_TASK_FRAGMENT_PARENT_INFO;

import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.MessageQueue;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentParentInfo;
import android.window.WindowContainerTransaction;

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Helper class to back up and restore the TaskFragmentOrganizer state, in order to resume
@@ -40,11 +50,21 @@ class BackupHelper {
    @NonNull
    private final SplitController mController;
    @NonNull
    private final SplitPresenter mPresenter;
    @NonNull
    private final BackupIdler mBackupIdler = new BackupIdler();
    private boolean mBackupIdlerScheduled;

    BackupHelper(@NonNull SplitController splitController, @NonNull Bundle savedState) {
    private final List<ParcelableTaskContainerData> mParcelableTaskContainerDataList =
            new ArrayList<>();
    private final ArrayMap<IBinder, TaskFragmentInfo> mTaskFragmentInfos = new ArrayMap<>();
    private final SparseArray<TaskFragmentParentInfo> mTaskFragmentParentInfos =
            new SparseArray<>();

    BackupHelper(@NonNull SplitController splitController, @NonNull SplitPresenter splitPresenter,
            @NonNull Bundle savedState) {
        mController = splitController;
        mPresenter = splitPresenter;

        if (!savedState.isEmpty()) {
            restoreState(savedState);
@@ -67,13 +87,13 @@ class BackupHelper {
        public boolean queueIdle() {
            synchronized (mController.mLock) {
                mBackupIdlerScheduled = false;
                startBackup();
                saveState();
            }
            return false;
        }
    }

    private void startBackup() {
    private void saveState() {
        final List<TaskContainer> taskContainers = mController.getTaskContainers();
        if (taskContainers.isEmpty()) {
            Log.w(TAG, "No task-container to back up");
@@ -97,13 +117,92 @@ class BackupHelper {
            return;
        }

        final List<ParcelableTaskContainerData> parcelableTaskContainerDataList =
                savedState.getParcelableArrayList(KEY_TASK_CONTAINERS,
                        ParcelableTaskContainerData.class);
        for (ParcelableTaskContainerData data : parcelableTaskContainerDataList) {
            final TaskContainer taskContainer = new TaskContainer(data, mController);
            if (DEBUG) Log.d(TAG, "Restoring task " + taskContainer.getTaskId());
            // TODO(b/289875940): implement the TaskContainer restoration.
        if (DEBUG) Log.d(TAG, "Start restoring saved-state");
        mParcelableTaskContainerDataList.addAll(savedState.getParcelableArrayList(
                KEY_TASK_CONTAINERS, ParcelableTaskContainerData.class));
        if (DEBUG) Log.d(TAG, "Retrieved tasks : " + mParcelableTaskContainerDataList.size());
        if (mParcelableTaskContainerDataList.isEmpty()) {
            return;
        }

        final List<TaskFragmentInfo> infos = savedState.getParcelableArrayList(
                KEY_RESTORE_TASK_FRAGMENTS_INFO, TaskFragmentInfo.class);
        for (TaskFragmentInfo info : infos) {
            if (DEBUG) Log.d(TAG, "Retrieved: " + info);
            mTaskFragmentInfos.put(info.getFragmentToken(), info);
            mPresenter.updateTaskFragmentInfo(info);
        }

        final List<TaskFragmentParentInfo> parentInfos = savedState.getParcelableArrayList(
                KEY_RESTORE_TASK_FRAGMENT_PARENT_INFO,
                TaskFragmentParentInfo.class);
        for (TaskFragmentParentInfo info : parentInfos) {
            if (DEBUG) Log.d(TAG, "Retrieved: " + info);
            mTaskFragmentParentInfos.put(info.getTaskId(), info);
        }
    }

    boolean hasPendingStateToRestore() {
        return !mParcelableTaskContainerDataList.isEmpty();
    }

    /**
     * Returns {@code true} if any of the {@link TaskContainer} is restored.
     * Otherwise, returns {@code false}.
     */
    boolean rebuildTaskContainers(@NonNull WindowContainerTransaction wct,
            @NonNull Set<EmbeddingRule> rules) {
        if (mParcelableTaskContainerDataList.isEmpty()) {
            return false;
        }

        if (DEBUG) Log.d(TAG, "Rebuilding TaskContainers.");
        final ArrayMap<String, EmbeddingRule> embeddingRuleMap = new ArrayMap<>();
        for (EmbeddingRule rule : rules) {
            embeddingRuleMap.put(rule.getTag(), rule);
        }

        boolean restoredAny = false;
        for (int i = mParcelableTaskContainerDataList.size() - 1; i >= 0; i--) {
            final ParcelableTaskContainerData parcelableTaskContainerData =
                    mParcelableTaskContainerDataList.get(i);
            final List<String> tags = parcelableTaskContainerData.getSplitRuleTags();
            if (!embeddingRuleMap.containsAll(tags)) {
                // has unknown tag, unable to restore.
                if (DEBUG) {
                    Log.d(TAG, "Rebuilding TaskContainer abort! Unknown Tag. Task#"
                            + parcelableTaskContainerData.mTaskId);
                }
                continue;
            }

            mParcelableTaskContainerDataList.remove(parcelableTaskContainerData);
            final TaskContainer taskContainer = new TaskContainer(parcelableTaskContainerData,
                    mController, mTaskFragmentInfos);
            if (DEBUG) Log.d(TAG, "Created TaskContainer " + taskContainer);
            mController.addTaskContainer(taskContainer.getTaskId(), taskContainer);

            for (ParcelableSplitContainerData splitData :
                    parcelableTaskContainerData.getParcelableSplitContainerDataList()) {
                final SplitRule rule = (SplitRule) embeddingRuleMap.get(splitData.mSplitRuleTag);
                assert rule != null;
                if (mController.getContainer(splitData.getPrimaryContainerToken()) != null
                        && mController.getContainer(splitData.getSecondaryContainerToken())
                        != null) {
                    taskContainer.addSplitContainer(
                            new SplitContainer(splitData, mController, rule));
                }
            }

            mController.onTaskFragmentParentRestored(wct, taskContainer.getTaskId(),
                    mTaskFragmentParentInfos.get(taskContainer.getTaskId()));
            restoredAny = true;
        }

        if (mParcelableTaskContainerDataList.isEmpty()) {
            mTaskFragmentParentInfos.clear();
            mTaskFragmentInfos.clear();
        }
        return restoredAny;
    }
}
 No newline at end of file
+2 −2
Original line number Diff line number Diff line
@@ -89,13 +89,13 @@ class ParcelableSplitContainerData implements Parcelable {
    };

    @NonNull
    private IBinder getPrimaryContainerToken() {
    IBinder getPrimaryContainerToken() {
        return mSplitContainer != null ? mSplitContainer.getPrimaryContainer().getToken()
                : mPrimaryContainerToken;
    }

    @NonNull
    private IBinder getSecondaryContainerToken() {
    IBinder getSecondaryContainerToken() {
        return mSplitContainer != null ? mSplitContainer.getSecondaryContainer().getToken()
                : mSecondaryContainerToken;
    }
+9 −0
Original line number Diff line number Diff line
@@ -108,6 +108,15 @@ class ParcelableTaskContainerData implements Parcelable {
                : mParcelableSplitContainerDataList;
    }

    @NonNull
    List<String> getSplitRuleTags() {
        final List<String> tags = new ArrayList<>();
        for (ParcelableSplitContainerData data : getParcelableSplitContainerDataList()) {
            tags.add(data.mSplitRuleTag);
        }
        return tags;
    }

    @Override
    public int describeContents() {
        return 0;
+19 −0
Original line number Diff line number Diff line
@@ -86,6 +86,25 @@ class SplitContainer {
        }
    }

    /** This is only used when restoring it from a {@link ParcelableSplitContainerData}. */
    SplitContainer(@NonNull ParcelableSplitContainerData parcelableData,
            @NonNull SplitController splitController, @NonNull SplitRule splitRule) {
        mParcelableData = parcelableData;
        mPrimaryContainer = splitController.getContainer(parcelableData.getPrimaryContainerToken());
        mSecondaryContainer = splitController.getContainer(
                parcelableData.getSecondaryContainerToken());
        mSplitRule = splitRule;
        mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes();
        mCurrentSplitAttributes = mDefaultSplitAttributes;

        if (shouldFinishPrimaryWithSecondary(splitRule)) {
            mSecondaryContainer.addContainerToFinishOnExit(mPrimaryContainer);
        }
        if (shouldFinishSecondaryWithPrimary(splitRule)) {
            mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer);
        }
    }

    void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) {
        if (!mParcelableData.mIsPrimaryContainerMutable) {
            throw new IllegalStateException("Cannot update primary TaskFragmentContainer");
Loading