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

Commit b8028fae authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Optimize attaching process for starting activity

Originally, any process start will need to go through entire
hierarchy, also, the ensureActivitiesVisible will be called if
an attaching process cannot match any activity. There can be
2 conditions:
1. The find-condition is not accurate enough, which misses the
   launching activity. Then the ensure-visible might refresh
   the latest visibility state and still launch the activity.
2. The attaching process doesn't contain activities, such as
   a pure receiver/service/provider process. Then the invocation
   is redundant and wasteful. Sometimes it may even make some
   noise to transition such as changing visibility randomly by
   unrelated process.

By storing the activities that are required to start process, it
can simply check a small list to know if the attaching process is
started for an activity. This can reduce 97% executing time of
attachApplication if the process is unrelated to activity.

Also ensureActivitiesVisible is no longer needed to catch the
missed cases by traversal. Because the starting activities are
known explicitly.

This also reduces twice duplicated request of process start from
completePause's resumeTop and ensureActivitiesVisible (it will have
extra invocations until ProcessList#startProcessLocked that checks
app.isPendingStart() to ignore). Because the pending records can
be found in startProcessAsync.

This also handles a corner case that if a process is never attached,
the launching app won't stuck on screen with a starting window. The
activity can run a close transition when receiving onProcessRemoved.
Originally it will be stuck because AMS only removes the process when
the start timeout is reached, there won't be a signal about process
died because it was not attached.

Bug: 297502610
Test: atest RootWindowContainerTests#testAttachApplication

Change-Id: I5e88f878eb35b9f5c0d385a3f85564255bd22445
parent 944de4f9
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -4477,6 +4477,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        getDisplayContent().mOpeningApps.remove(this);
        getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
        mWmService.mSnapshotController.onAppRemoved(this);
        mAtmService.mStartingProcessActivities.remove(this);

        mTaskSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
        mTaskSupervisor.mStoppingActivities.remove(this);
@@ -6219,6 +6220,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    }

    boolean shouldBeVisible() {
        return shouldBeVisible(false /* ignoringKeyguard */);
    }

    boolean shouldBeVisible(boolean ignoringKeyguard) {
        final Task task = getTask();
        if (task == null) {
            return false;
@@ -6226,7 +6231,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A

        final boolean behindOccludedContainer = !task.shouldBeVisible(null /* starting */)
                || task.getOccludingActivityAbove(this) != null;
        return shouldBeVisible(behindOccludedContainer, false /* ignoringKeyguard */);
        return shouldBeVisible(behindOccludedContainer, ignoringKeyguard);
    }

    void makeVisibleIfNeeded(ActivityRecord starting, boolean reportToClient) {
@@ -10507,8 +10512,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        if (!getTurnScreenOnFlag()) {
            return false;
        }
        final Task rootTask = getRootTask();
        return mCurrentLaunchCanTurnScreenOn && rootTask != null
        return mCurrentLaunchCanTurnScreenOn
                && mTaskSupervisor.getKeyguardController().checkKeyguardVisibility(this);
    }

+27 −1
Original line number Diff line number Diff line
@@ -390,6 +390,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {

    final VisibleActivityProcessTracker mVisibleActivityProcessTracker;

    /** The starting activities which are waiting for their processes to attach. */
    final ArrayList<ActivityRecord> mStartingProcessActivities = new ArrayList<>();

    /* Global service lock used by the package the owns this service. */
    final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock();
    /**
@@ -4322,6 +4325,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
            if (mDemoteTopAppReasons != 0) {
                pw.println("  mDemoteTopAppReasons=" + mDemoteTopAppReasons);
            }
            if (!mStartingProcessActivities.isEmpty()) {
                pw.println("  mStartingProcessActivities=" + mStartingProcessActivities);
            }
        }

        if (!printedAnything) {
@@ -5169,6 +5175,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {

    void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
            String hostingType) {
        if (!mStartingProcessActivities.contains(activity)) {
            mStartingProcessActivities.add(activity);
        } else if (mProcessNames.get(
                activity.processName, activity.info.applicationInfo.uid) != null) {
            // The process is already starting. Wait for it to attach.
            return;
        }
        try {
            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:"
@@ -6165,7 +6178,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        @Override
        public void onProcessRemoved(String name, int uid) {
            synchronized (mGlobalLockWithoutBoost) {
                mProcessNames.remove(name, uid);
                final WindowProcessController proc = mProcessNames.remove(name, uid);
                if (proc != null && !mStartingProcessActivities.isEmpty()) {
                    for (int i = mStartingProcessActivities.size() - 1; i >= 0; i--) {
                        final ActivityRecord r = mStartingProcessActivities.get(i);
                        if (uid == r.info.applicationInfo.uid && name.equals(r.processName)) {
                            Slog.w(TAG, proc + " is removed with pending start " + r);
                            mStartingProcessActivities.remove(i);
                            // If visible, finish it to avoid getting stuck on screen.
                            if (r.isVisibleRequested()) {
                                r.finishIfPossible("starting-proc-removed", false /* oomAdj */);
                            }
                        }
                    }
                }
            }
        }

+32 −70
Original line number Diff line number Diff line
@@ -72,7 +72,6 @@ import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER
import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
import static com.android.server.wm.Task.REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -270,8 +269,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
    private int mTmpTaskLayerRank;
    private final RankTaskLayersRunnable mRankTaskLayersRunnable = new RankTaskLayersRunnable();

    private final AttachApplicationHelper mAttachApplicationHelper = new AttachApplicationHelper();

    private String mDestroyAllActivitiesReason;
    private final Runnable mDestroyAllActivitiesRunnable = new Runnable() {
        @Override
@@ -1838,11 +1835,39 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
    }

    boolean attachApplication(WindowProcessController app) throws RemoteException {
        final ArrayList<ActivityRecord> activities = mService.mStartingProcessActivities;
        RemoteException remoteException = null;
        boolean hasActivityStarted = false;
        for (int i = activities.size() - 1; i >= 0; i--) {
            final ActivityRecord r = activities.get(i);
            if (app.mUid != r.info.applicationInfo.uid || !app.mName.equals(r.processName)) {
                // The attaching process does not match the starting activity.
                continue;
            }
            // Consume the pending record.
            activities.remove(i);
            final TaskFragment tf = r.getTaskFragment();
            if (tf == null || r.finishing || r.app != null
                    // Ignore keyguard because the app may use show-when-locked when creating.
                    || !r.shouldBeVisible(true /* ignoringKeyguard */)
                    || !r.showToCurrentUser()) {
                continue;
            }
            try {
            return mAttachApplicationHelper.process(app);
        } finally {
            mAttachApplicationHelper.reset();
                final boolean canResume = r.isFocusable() && r == tf.topRunningActivity();
                if (mTaskSupervisor.realStartActivityLocked(r, app, canResume,
                        true /* checkConfig */)) {
                    hasActivityStarted = true;
                }
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception in new process when starting " + r, e);
                remoteException = e;
            }
        }
        if (remoteException != null) {
            throw remoteException;
        }
        return hasActivityStarted;
    }

    /**
@@ -3742,67 +3767,4 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
            }
        }
    }

    private class AttachApplicationHelper implements Consumer<Task>, Predicate<ActivityRecord> {
        private boolean mHasActivityStarted;
        private RemoteException mRemoteException;
        private WindowProcessController mApp;
        private ActivityRecord mTop;

        void reset() {
            mHasActivityStarted = false;
            mRemoteException = null;
            mApp = null;
            mTop = null;
        }

        boolean process(WindowProcessController app) throws RemoteException {
            mApp = app;
            for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
                getChildAt(displayNdx).forAllRootTasks(this);
                if (mRemoteException != null) {
                    throw mRemoteException;
                }
            }
            if (!mHasActivityStarted) {
                ensureActivitiesVisible();
            }
            return mHasActivityStarted;
        }

        @Override
        public void accept(Task rootTask) {
            if (mRemoteException != null) {
                return;
            }
            if (rootTask.getVisibility(null /* starting */)
                    == TASK_FRAGMENT_VISIBILITY_INVISIBLE) {
                return;
            }
            mTop = rootTask.topRunningActivity();
            rootTask.forAllActivities(this);
        }

        @Override
        public boolean test(ActivityRecord r) {
            if (r.finishing || !r.showToCurrentUser() || !r.visibleIgnoringKeyguard
                    || r.app != null || mApp.mUid != r.info.applicationInfo.uid
                    || !mApp.mName.equals(r.processName)) {
                return false;
            }

            try {
                if (mTaskSupervisor.realStartActivityLocked(r, mApp,
                        mTop == r && r.getTask().canBeResumed(r) /* andResume */,
                        true /* checkConfig */)) {
                    mHasActivityStarted = true;
                }
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception in new application when starting activity " + mTop, e);
                mRemoteException = e;
                return true;
            }
            return false;
        }
    }
}
+19 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
@@ -237,6 +238,24 @@ public class RootWindowContainerTests extends WindowTestsBase {
        assertFalse(wpc.hasActivities());
    }

    @Test
    public void testAttachApplication() {
        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
        activity.detachFromProcess();
        mAtm.startProcessAsync(activity, false /* knownToBeDead */,
                true /* isTop */, "test" /* hostingType */);
        final WindowProcessController proc = mSystemServicesTestRule.addProcess(
                activity.packageName, activity.processName,
                6789 /* pid */, activity.info.applicationInfo.uid);
        try {
            mRootWindowContainer.attachApplication(proc);
            verify(mSupervisor).realStartActivityLocked(eq(activity), eq(proc), anyBoolean(),
                    anyBoolean());
        } catch (RemoteException e) {
            e.rethrowAsRuntimeException();
        }
    }

    /**
     * This test ensures that we do not try to restore a task based off an invalid task id. We
     * should expect {@code null} to be returned in this case.