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

Commit b9065f0e authored by Louis Chang's avatar Louis Chang
Browse files

Fix ActivityEmbedding flicker test flakiness

The AE backstack restoration is now enabled by default for field test.
The test application was not following the guideline and was adding
the embedding rules after Activity created. Adding more check to
abort the restoration as early as possible. The changes includes:

- Abort the restoration earlier if the Task is no longer existed
  in the WM Core.
- Also checking if the restoration should be aborted in #postCreated
- Start the task root activity only if the Task is empty after the
  legacy TaskFragments are removed.

Bug: 371083896
Bug: 289875940
Test: OpenActivityEmbeddingSecondaryToSplitTest
Flag: com.android.window.flags.ae_back_stack_restore

Change-Id: Iefebfc6b6e70edddeb0e078429b65b826885daef
parent a3bea3e2
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -3841,6 +3841,18 @@ public final class ActivityThread extends ClientTransactionHandler
        return activityRecord != null ? activityRecord.activity : null;
    }

    /**
     * Returns the most recent created activity that's still running.
     */
    @Nullable
    public Activity getLastCreatedActivity() {
        if (mActivities.isEmpty()) {
            return null;
        }

        return mActivities.valueAt(mActivities.size() - 1).activity;
    }

    @Override
    public ActivityClientRecord getActivityClient(IBinder token) {
        return mActivities.get(token);
+26 −1
Original line number Diff line number Diff line
@@ -148,8 +148,12 @@ class BackupHelper {
            final TaskFragmentInfo info = mTaskFragmentInfos.valueAt(i);
            mPresenter.deleteTaskFragment(wct, info.getFragmentToken());
        }
        mPresenter.setSavedState(new Bundle());

        removeSavedState();
    }

    private void removeSavedState() {
        mPresenter.setSavedState(new Bundle());
        mParcelableTaskContainerDataList.clear();
        mTaskFragmentInfos.clear();
        mTaskFragmentParentInfos.clear();
@@ -169,6 +173,19 @@ class BackupHelper {
            return false;
        }

        if (mTaskFragmentParentInfos.size() == 0) {
            // No Task left in the WM hierarchy, remove the states and no need to restore.
            if (DEBUG) Log.d(TAG, "Remove save states due to no task to restore.");
            removeSavedState();
            return false;
        }

        final ArrayList<Integer> taskIdsInSystem = new ArrayList<>();
        for (int i = mTaskFragmentParentInfos.size() - 1; i >= 0; --i) {
            final TaskFragmentParentInfo parentInfo = mTaskFragmentParentInfos.valueAt(i);
            taskIdsInSystem.add(parentInfo.getTaskId());
        }

        if (DEBUG) Log.d(TAG, "Rebuilding TaskContainers.");
        final ArrayMap<String, EmbeddingRule> embeddingRuleMap = new ArrayMap<>();
        for (EmbeddingRule rule : rules) {
@@ -190,6 +207,14 @@ class BackupHelper {
            }

            mParcelableTaskContainerDataList.remove(parcelableTaskContainerData);
            if (!taskIdsInSystem.contains(parcelableTaskContainerData.mTaskId)) {
                if (DEBUG) {
                    Log.d(TAG, "Rebuilding TaskContainer abort! Not existed. Task#"
                            + parcelableTaskContainerData.mTaskId);
                }
                continue;
            }

            final TaskContainer taskContainer = new TaskContainer(parcelableTaskContainerData,
                    mController, mTaskFragmentInfos);
            if (DEBUG) Log.d(TAG, "Created TaskContainer " + taskContainer);
+57 −27
Original line number Diff line number Diff line
@@ -239,6 +239,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        mActivityStartMonitor = new ActivityStartMonitor();
        instrumentation.addMonitor(mActivityStartMonitor);
        foldingFeatureProducer.addDataChangedCallback(new FoldingFeatureListener());

        synchronized (mLock) {
            // Abort the restoration if any and the application already has running activities.
            abortRebuildingTaskContainersIfNeeded(null /* launchingActivity */);
        }
    }

    private class FoldingFeatureListener
@@ -285,6 +290,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                return;
            }

            if (abortRebuildingTaskContainersIfNeeded(null /* launchingActivity */)) {
                return;
            }

            try {
                final TransactionRecord transactionRecord =
                        mTransactionManager.startNewTransaction();
@@ -2883,31 +2892,28 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }
    }

    private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter {
    @GuardedBy("mLock")
    private boolean abortRebuildingTaskContainersIfNeeded(@Nullable Activity launchingActivity) {
        if (mPresenter == null || !mPresenter.isWaitingToRebuildTaskContainers()) {
            return false;
        }

        @Override
        public void onActivityPreCreated(@NonNull Activity activity,
                @Nullable Bundle savedInstanceState) {
            if (activity.isChild()) {
                // Skip Activity that is child of another Activity (ActivityGroup) because it's
                // window will just be a child of the parent Activity window.
                return;
        final ActivityThread activityThread = ActivityThread.currentActivityThread();
        if (activityThread == null) {
            return false;
        }
            synchronized (mLock) {
                if (mPresenter.isWaitingToRebuildTaskContainers()) {
                    Log.w(TAG, "Rebuilding aborted, clean up and restart");

        final Activity lastCreatedActivity = activityThread.getLastCreatedActivity();
        final Activity activity =
                launchingActivity != null ? launchingActivity : lastCreatedActivity;
        if (activity == null) {
            return false;
        }

        Log.w(TAG, "Rebuilding aborted, clean up.");

        // Retrieve the Task intent.
        final int taskId = getTaskId(activity);
                    Intent taskIntent = null;
                    final ActivityManager am = activity.getSystemService(ActivityManager.class);
                    final List<ActivityManager.AppTask> appTasks = am.getAppTasks();
                    for (ActivityManager.AppTask appTask : appTasks) {
                        if (appTask.getTaskInfo().taskId == taskId) {
                            taskIntent = appTask.getTaskInfo().baseIntent.cloneFilter();
                            break;
                        }
                    }

        // Clean up and abort the restoration
        // TODO(b/369488857): also to remove the non-organized activities in the Task?
@@ -2917,10 +2923,34 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        mPresenter.abortTaskContainerRebuilding(wct);
        transactionRecord.apply(false /* shouldApplyIndependently */);

                    // Start the Task root activity.
                    if (taskIntent != null) {
                        activity.startActivity(taskIntent);
        // Start the Task root activity if the task is now empty.
        ActivityManager.RecentTaskInfo taskInfo = null;
        final ActivityManager am = activity.getSystemService(ActivityManager.class);
        final List<ActivityManager.AppTask> appTasks = am.getAppTasks();
        for (ActivityManager.AppTask appTask : appTasks) {
            if (appTask.getTaskInfo().taskId == taskId) {
                taskInfo = appTask.getTaskInfo();
                break;
            }
        }
        if (taskInfo != null && !taskInfo.isRunning) {
            activity.startActivity(taskInfo.baseIntent.cloneFilter());
        }
        return true;
    }

    private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter {

        @Override
        public void onActivityPreCreated(@NonNull Activity activity,
                @Nullable Bundle savedInstanceState) {
            if (activity.isChild()) {
                // Skip Activity that is child of another Activity (ActivityGroup) because it's
                // window will just be a child of the parent Activity window.
                return;
            }
            synchronized (mLock) {
                if (abortRebuildingTaskContainersIfNeeded(activity)) {
                    return;
                }