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

Commit 0f8a5212 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Consolidate waiting list of visible and launched activity

Previously, when "am start -W $activityName" launches a new activity,
it just blindly catches for any next drawn activity. E.g. press home
key before the target activity is drawn, the launch result will show
the result of home.

The case of launching existing activity was handled separately because
the target component name is usually known, but the case of new
activity is unknown because it may be a trampoline that launches other
activities. Now the WaitInfo can track the launch sequence according
to the LaunchingState provided by ActivityMetricsLogger. So both the
cases can share the same logic with distinguishing the individual
launch events.

If a launch event is aborted, as the above example of returning to
home, ActivityMetricsLogger will know the original task is no longer
visible and notify to stop waiting the result. The launch state of
result will be set to LAUNCH_STATE_UNKNOWN with the original target
component name.

Bug: 129868870
Test: CtsWindowManagerDeviceTestCases:AmStartOptionsTests
      ActivityTaskSupervisorTests#testReportWaitingActivityLaunched

Change-Id: I201a275c780749cba570319d522b7921612c6ca3
parent a0334be8
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -174,6 +174,10 @@ class ActivityMetricsLogger {
        boolean allDrawn() {
            return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.allDrawn();
        }

        boolean contains(ActivityRecord r) {
            return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.contains(r);
        }
    }

    /** The information created when an activity is confirmed to be launched. */
@@ -793,6 +797,7 @@ class ActivityMetricsLogger {

        stopLaunchTrace(info);
        if (abort) {
            mSupervisor.stopWaitingForActivityVisible(info.mLastLaunchedActivity);
            launchObserverNotifyActivityLaunchCancelled(info);
        } else {
            if (info.isInterestingToLoggerAndObserver()) {
+2 −3
Original line number Diff line number Diff line
@@ -5421,7 +5421,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        final TransitionInfoSnapshot info = mTaskSupervisor
            .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
        if (info != null) {
            mTaskSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
            mTaskSupervisor.reportActivityLaunched(false /* timeout */, this,
                    info.windowsFullyDrawnDelayMs, info.getLaunchState());
        }
    }
@@ -5462,9 +5462,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        // so there is no valid info. But if it is the current top activity (e.g. sleeping), the
        // invalid state is still reported to make sure the waiting result is notified.
        if (validInfo || this == getDisplayArea().topRunningActivity()) {
            mTaskSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
            mTaskSupervisor.reportActivityLaunched(false /* timeout */, this,
                    windowsDrawnDelayMs, launchState);
            mTaskSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs, launchState);
        }
        finishLaunchTickingLocked();
        if (task != null) {
+21 −44
Original line number Diff line number Diff line
@@ -707,8 +707,12 @@ class ActivityStarter {
                // WaitResult.
                mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,
                        mLastStartActivityRecord, originalOptions);
                return getExternalResult(mRequest.waitResult == null ? res
                        : waitForResult(res, mLastStartActivityRecord));
                if (mRequest.waitResult != null) {
                    mRequest.waitResult.result = res;
                    res = waitResultIfNeeded(mRequest.waitResult, mLastStartActivityRecord,
                            launchingState);
                }
                return getExternalResult(res);
            }
        } finally {
            onExecutionComplete();
@@ -793,48 +797,21 @@ class ActivityStarter {
    /**
     * Wait for activity launch completes.
     */
    private int waitForResult(int res, ActivityRecord r) {
        mRequest.waitResult.result = res;
        switch(res) {
            case START_SUCCESS: {
                mSupervisor.mWaitingActivityLaunched.add(mRequest.waitResult);
                do {
                    try {
                        mService.mGlobalLock.wait();
                    } catch (InterruptedException e) {
                    }
                } while (mRequest.waitResult.result != START_TASK_TO_FRONT
                        && !mRequest.waitResult.timeout && mRequest.waitResult.who == null);
                if (mRequest.waitResult.result == START_TASK_TO_FRONT) {
                    res = START_TASK_TO_FRONT;
                }
                break;
            }
            case START_DELIVERED_TO_TOP: {
                mRequest.waitResult.timeout = false;
                mRequest.waitResult.who = r.mActivityComponent;
                mRequest.waitResult.totalTime = 0;
                break;
            }
            case START_TASK_TO_FRONT: {
                // ActivityRecord may represent a different activity, but it should not be
                // in the resumed state.
                if (r.nowVisible && r.isState(RESUMED)) {
                    mRequest.waitResult.timeout = false;
                    mRequest.waitResult.who = r.mActivityComponent;
                    mRequest.waitResult.totalTime = 0;
                } else {
                    mSupervisor.waitActivityVisible(r.mActivityComponent, mRequest.waitResult);
                    // Note: the timeout variable is not currently not ever set.
                    do {
                        try {
                            mService.mGlobalLock.wait();
                        } catch (InterruptedException e) {
                        }
                    } while (!mRequest.waitResult.timeout && mRequest.waitResult.who == null);
                }
                break;
    private int waitResultIfNeeded(WaitResult waitResult, ActivityRecord r,
            LaunchingState launchingState) {
        final int res = waitResult.result;
        if (res == START_DELIVERED_TO_TOP
                || (res == START_TASK_TO_FRONT && r.nowVisible && r.isState(RESUMED))) {
            // The activity should already be visible, so nothing to wait.
            waitResult.timeout = false;
            waitResult.who = r.mActivityComponent;
            waitResult.totalTime = 0;
            return res;
        }
        mSupervisor.waitActivityVisibleOrLaunched(waitResult, r, launchingState);
        if (res == START_SUCCESS && waitResult.result == START_TASK_TO_FRONT) {
            // A trampoline activity is launched and it brings another existing activity to front.
            return START_TASK_TO_FRONT;
        }
        return res;
    }
+63 −75
Original line number Diff line number Diff line
@@ -266,11 +266,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
     */
    private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20);

    /** List of processes waiting to find out when a specific activity becomes visible. */
    private final ArrayList<WaitInfo> mWaitingForActivityVisible = new ArrayList<>();

    /** List of processes waiting to find out about the next launched activity. */
    final ArrayList<WaitResult> mWaitingActivityLaunched = new ArrayList<>();
    /** List of requests waiting for the target activity to be launched or visible. */
    private final ArrayList<WaitInfo> mWaitingActivityLaunched = new ArrayList<>();

    /** List of activities that are ready to be stopped, but waiting for the next activity to
     * settle down before doing so. */
@@ -552,9 +549,21 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
        return candidateTaskId;
    }

    void waitActivityVisible(ComponentName name, WaitResult result) {
        final WaitInfo waitInfo = new WaitInfo(name, result);
        mWaitingForActivityVisible.add(waitInfo);
    void waitActivityVisibleOrLaunched(WaitResult w, ActivityRecord r,
            LaunchingState launchingState) {
        if (w.result != ActivityManager.START_TASK_TO_FRONT
                && w.result != ActivityManager.START_SUCCESS) {
            // Not a result code that can make activity visible or launched.
            return;
        }
        final WaitInfo waitInfo = new WaitInfo(w, r.mActivityComponent, launchingState);
        mWaitingActivityLaunched.add(waitInfo);
        do {
            try {
                mService.mGlobalLock.wait();
            } catch (InterruptedException ignored) {
            }
        } while (mWaitingActivityLaunched.contains(waitInfo));
    }

    void cleanupActivity(ActivityRecord r) {
@@ -568,23 +577,25 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {

    /** There is no valid launch time, just stop waiting. */
    void stopWaitingForActivityVisible(ActivityRecord r) {
        stopWaitingForActivityVisible(r, WaitResult.INVALID_DELAY, WaitResult.LAUNCH_STATE_UNKNOWN);
        reportActivityLaunched(false /* timeout */, r, WaitResult.INVALID_DELAY,
                WaitResult.LAUNCH_STATE_UNKNOWN);
    }

    void stopWaitingForActivityVisible(ActivityRecord r, long totalTime,
    void reportActivityLaunched(boolean timeout, ActivityRecord r, long totalTime,
            @WaitResult.LaunchState int launchState) {
        boolean changed = false;
        for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
            final WaitInfo w = mWaitingForActivityVisible.get(i);
            if (w.matches(r.mActivityComponent)) {
                final WaitResult result = w.getResult();
                changed = true;
                result.timeout = false;
                result.who = w.getComponent();
                result.totalTime = totalTime;
                result.launchState = launchState;
                mWaitingForActivityVisible.remove(w);
        for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
            final WaitInfo info = mWaitingActivityLaunched.get(i);
            if (!info.matches(r)) {
                continue;
            }
            final WaitResult w = info.mResult;
            w.timeout = timeout;
            w.who = r.mActivityComponent;
            w.totalTime = totalTime;
            w.launchState = launchState;
            mWaitingActivityLaunched.remove(i);
            changed = true;
        }
        if (changed) {
            mService.mGlobalLock.notifyAll();
@@ -603,38 +614,18 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
        boolean changed = false;

        for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
            WaitResult w = mWaitingActivityLaunched.remove(i);
            if (w.who == null) {
                changed = true;
            final WaitInfo info = mWaitingActivityLaunched.get(i);
            if (!info.matches(r)) {
                continue;
            }
            final WaitResult w = info.mResult;
            w.result = result;

            if (result == START_DELIVERED_TO_TOP) {
                // Unlike START_TASK_TO_FRONT, When an intent is delivered to top, there
                // will be no followup launch signals. Assign the result and launched component.
                if (result == START_DELIVERED_TO_TOP) {
                w.who = r.mActivityComponent;
                }
            }
        }

        if (changed) {
            mService.mGlobalLock.notifyAll();
        }
    }

    void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long totalTime,
            @WaitResult.LaunchState int launchState) {
        boolean changed = false;
        for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
            WaitResult w = mWaitingActivityLaunched.remove(i);
            if (w.who == null) {
                mWaitingActivityLaunched.remove(i);
                changed = true;
                w.timeout = timeout;
                if (r != null) {
                    w.who = new ComponentName(r.info.packageName, r.info.name);
                }
                w.totalTime = totalTime;
                w.launchState = launchState;
                // Do not modify w.result.
            }
        }
        if (changed) {
@@ -1295,8 +1286,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
            mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
            r.finishLaunchTickingLocked();
            if (fromTimeout) {
                reportActivityLaunchedLocked(fromTimeout, r, INVALID_DELAY,
                        -1 /* launchState */);
                reportActivityLaunched(fromTimeout, r, INVALID_DELAY, -1 /* launchState */);
            }

            // This is a hack to semi-deal with a race condition
@@ -1940,14 +1930,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
        pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
        pw.println(prefix + "mUserRootTaskInFront=" + mRootWindowContainer.mUserRootTaskInFront);
        pw.println(prefix + "mVisibilityTransactionDepth=" + mVisibilityTransactionDepth);
        if (!mWaitingForActivityVisible.isEmpty()) {
            pw.println(prefix + "mWaitingForActivityVisible=");
            for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) {
                pw.print(prefix + prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
            }
        }
        pw.print(prefix); pw.print("isHomeRecentsComponent=");
        pw.println(mRecentTasks.isRecentsComponentHomeActivity(mRootWindowContainer.mCurrentUser));
        if (!mWaitingActivityLaunched.isEmpty()) {
            pw.println(prefix + "mWaitingActivityLaunched=");
            for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
                mWaitingActivityLaunched.get(i).dump(pw, prefix + "  ");
            }
        }
        pw.println();
    }

@@ -2602,32 +2592,30 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
    /**
     * Internal container to store a match qualifier alongside a WaitResult.
     */
    static class WaitInfo {
        private final ComponentName mTargetComponent;
        private final WaitResult mResult;

        WaitInfo(ComponentName targetComponent, WaitResult result) {
            this.mTargetComponent = targetComponent;
            this.mResult = result;
        }

        public boolean matches(ComponentName targetComponent) {
            return mTargetComponent == null || mTargetComponent.equals(targetComponent);
        }
    private static class WaitInfo {
        final WaitResult mResult;
        final ComponentName mTargetComponent;
        /**
         * The target component may not be the final drawn activity. The launching state is managed
         * by {@link ActivityMetricsLogger} that can track consecutive launching sequence.
         */
        final LaunchingState mLaunchingState;

        public WaitResult getResult() {
            return mResult;
        WaitInfo(WaitResult result, ComponentName component, LaunchingState launchingState) {
            mResult = result;
            mTargetComponent = component;
            mLaunchingState = launchingState;
        }

        public ComponentName getComponent() {
            return mTargetComponent;
        boolean matches(ActivityRecord r) {
            return mTargetComponent.equals(r.mActivityComponent) || mLaunchingState.contains(r);
        }

        public void dump(PrintWriter pw, String prefix) {
        void dump(PrintWriter pw, String prefix) {
            pw.println(prefix + "WaitInfo:");
            pw.println(prefix + "  mTargetComponent=" + mTargetComponent);
            pw.println(prefix + "  mResult=");
            mResult.dump(pw, prefix);
            mResult.dump(pw, prefix + "    ");
        }
    }
}
+0 −9
Original line number Diff line number Diff line
@@ -5440,14 +5440,6 @@ class Task extends WindowContainer<WindowContainer> {
        r.completeResumeLocked();
    }

    private void clearLaunchTime(ActivityRecord r) {
        // Make sure that there is no activity waiting for this to launch.
        if (!mTaskSupervisor.mWaitingActivityLaunched.isEmpty()) {
            mTaskSupervisor.removeIdleTimeoutForActivity(r);
            mTaskSupervisor.scheduleIdleTimeout(r);
        }
    }

    void awakeFromSleepingLocked() {
        if (!isLeafTask()) {
            forAllLeafTasks((task) -> task.awakeFromSleepingLocked(),
@@ -5590,7 +5582,6 @@ class Task extends WindowContainer<WindowContainer> {
        mLastNoHistoryActivity = prev.isNoHistory() ? prev : null;
        prev.setState(PAUSING, "startPausingLocked");
        prev.getTask().touchActiveTime();
        clearLaunchTime(prev);

        mAtmService.updateCpuStats();

Loading