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

Commit 747432f3 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Avoid canceling WaitInfo by trampoline activity

An invisible trampoline activity may not animate, so it may be
destroyed earlier than the next activity launched by the trampoline.
If the WaitInfo aborts by matching the component name of the
trampoline when it is destroyed, the launch time becomes unknown.

The case happens when the trampoline activity and the next main
activity are in different tasks. Because both the activity and its
parent are not animating, it will be scheduled to destroy.

Also keep invisible initializing pending drawn records when the
transition is started, because it depends on the drawn time of
starting window, the next activity may not yet update visibility.

Bug: 193112389
Test: AmStartOptionsTests
      ActivityTaskSupervisorTests# \
        testReportWaitingActivityLaunched
      ActivityMetricsLaunchObserverTests# \
        testDoNotCountInvisibleActivityToBeDrawn
Change-Id: I53c2aa6fd604bb12d539176296663a40ba762d22
parent 7e62af12
Loading
Loading
Loading
Loading
+11 −4
Original line number Diff line number Diff line
@@ -187,6 +187,10 @@ class ActivityMetricsLogger {
            return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.allDrawn();
        }

        boolean hasActiveTransitionInfo() {
            return mAssociatedTransitionInfo != null;
        }

        boolean contains(ActivityRecord r) {
            return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.contains(r);
        }
@@ -336,10 +340,11 @@ class ActivityMetricsLogger {
        }

        /** Only keep the records which can be drawn. */
        void updatePendingDraw() {
        void updatePendingDraw(boolean keepInitializing) {
            for (int i = mPendingDrawActivities.size() - 1; i >= 0; i--) {
                final ActivityRecord r = mPendingDrawActivities.get(i);
                if (!r.mVisibleRequested) {
                if (!r.mVisibleRequested
                        && !(keepInitializing && r.isState(ActivityRecord.State.INITIALIZING))) {
                    if (DEBUG_METRICS) Slog.i(TAG, "Discard pending draw " + r);
                    mPendingDrawActivities.remove(i);
                }
@@ -663,7 +668,7 @@ class ActivityMetricsLogger {
        // visible such as after the top task is finished.
        for (int i = mTransitionInfoList.size() - 2; i >= 0; i--) {
            final TransitionInfo prevInfo = mTransitionInfoList.get(i);
            prevInfo.updatePendingDraw();
            prevInfo.updatePendingDraw(false /* keepInitializing */);
            if (prevInfo.allDrawn()) {
                abort(prevInfo, "nothing will be drawn");
            }
@@ -749,7 +754,9 @@ class ActivityMetricsLogger {
            info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs);
            info.mReason = activityToReason.valueAt(index);
            info.mLoggedTransitionStarting = true;
            info.updatePendingDraw();
            // Do not remove activity in initializing state because the transition may be started
            // by starting window. The initializing activity may be requested to visible soon.
            info.updatePendingDraw(true /* keepInitializing */);
            if (info.allDrawn()) {
                done(false /* abort */, info, "notifyTransitionStarting - all windows drawn",
                        timestampNs);
+4 −1
Original line number Diff line number Diff line
@@ -2596,7 +2596,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
        }

        boolean matches(ActivityRecord r) {
            return mTargetComponent.equals(r.mActivityComponent) || mLaunchingState.contains(r);
            if (!mLaunchingState.hasActiveTransitionInfo()) {
                return mTargetComponent.equals(r.mActivityComponent);
            }
            return mLaunchingState.contains(r);
        }

        void dump(PrintWriter pw, String prefix) {
+1 −0
Original line number Diff line number Diff line
@@ -404,6 +404,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {

        // Another round without setting visibility of the trampoline activity.
        onActivityLaunchedTrampoline();
        mTrampolineActivity.setState(ActivityRecord.State.PAUSING, "test");
        notifyWindowsDrawn(mTopActivity);
        // If the transition can start, the invisible activities should be discarded and the launch
        // event be reported successfully.
+7 −4
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import androidx.test.filters.MediumTest;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;

import java.util.concurrent.TimeUnit;

@@ -108,16 +109,18 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
        final ActivityMetricsLogger.LaunchingState launchingState =
                new ActivityMetricsLogger.LaunchingState();
        spyOn(launchingState);
        doReturn(true).when(launchingState).contains(eq(secondActivity));
        doReturn(true).when(launchingState).hasActiveTransitionInfo();
        doReturn(true).when(launchingState).contains(
                ArgumentMatchers.argThat(r -> r == firstActivity || r == secondActivity));
        // The test case already runs inside global lock, so above thread can only execute after
        // this waiting method that releases the lock.
        mSupervisor.waitActivityVisibleOrLaunched(taskToFrontWait, firstActivity, launchingState);

        // Assert that the thread is finished.
        assertTrue(condition.block(TIMEOUT_MS));
        assertEquals(taskToFrontWait.result, START_TASK_TO_FRONT);
        assertEquals(taskToFrontWait.who, secondActivity.mActivityComponent);
        assertEquals(taskToFrontWait.launchState, WaitResult.LAUNCH_STATE_HOT);
        assertEquals(START_TASK_TO_FRONT, taskToFrontWait.result);
        assertEquals(secondActivity.mActivityComponent, taskToFrontWait.who);
        assertEquals(WaitResult.LAUNCH_STATE_HOT, taskToFrontWait.launchState);
        // START_TASK_TO_FRONT means that another component will be visible, so the component
        // should not be assigned as the first activity.
        assertNull(launchedComponent[0]);