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

Commit 03cdd32a authored by Bryce Lee's avatar Bryce Lee Committed by Android (Google) Code Review
Browse files

Merge "Recycle ActivityStarter instances."

parents dce16ffd daa91e4a
Loading
Loading
Loading
Loading
+15 −4
Original line number Diff line number Diff line
@@ -105,7 +105,8 @@ public class ActivityStartController {
    /**
     * TODO(b/64750076): Capture information necessary for dump and
     * {@link #postStartActivityProcessingForLastStarter} rather than keeping the entire object
     * around */
     * around
     */
    private ActivityStarter mLastStarter;

    ActivityStartController(ActivityManagerService service) {
@@ -130,10 +131,16 @@ public class ActivityStartController {
     *         considered invalid and no longer modified or used.
     */
    ActivityStarter obtainStarter(Intent intent, String reason) {
        final ActivityStarter starter = mFactory.obtainStarter();
        mLastStarter = starter;
        return mFactory.obtain().setIntent(intent).setReason(reason);
    }

    void onExecutionComplete(ActivityStarter starter) {
        if (mLastStarter == null) {
            mLastStarter = mFactory.obtain();
        }

        return starter.setIntent(intent).setReason(reason);
        mLastStarter.set(starter);
        mFactory.recycle(starter);
    }

    /**
@@ -142,6 +149,10 @@ public class ActivityStartController {
     */
    void postStartActivityProcessingForLastStarter(ActivityRecord r, int result,
            ActivityStack targetStack) {
        if (mLastStarter == null) {
            return;
        }

        mLastStarter.postStartActivityProcessing(r, result, targetStack);
    }

+250 −35
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ import android.os.UserManager;
import android.service.voice.IVoiceInteractionSession;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Pools.SynchronizedPool;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
@@ -131,7 +132,6 @@ class ActivityStarter {
    private final ActivityManagerService mService;
    private final ActivityStackSupervisor mSupervisor;
    private final ActivityStartInterceptor mInterceptor;

    private final ActivityStartController mController;

    // Share state variable among methods when starting an activity.
@@ -209,18 +209,34 @@ class ActivityStarter {
         *                   this instance.
         * @return an {@link ActivityStarter}
         */
        ActivityStarter obtainStarter();
        ActivityStarter obtain();

        /**
         * Recycles a starter for reuse.
         */
        void recycle(ActivityStarter starter);
    }

    /**
     * Default implementation of {@link StarterFactory}.
     */
    static class DefaultFactory implements Factory {
        /**
         * The maximum count of starters that should be active at one time:
         * 1. last ran starter (for logging and post activity processing)
         * 2. current running starter
         * 3. starter from re-entry in (2)
         */
        private final int MAX_STARTER_COUNT = 3;

        private ActivityStartController mController;
        private ActivityManagerService mService;
        private ActivityStackSupervisor mSupervisor;
        private ActivityStartInterceptor mInterceptor;

        private SynchronizedPool<ActivityStarter> mStarterPool =
                new SynchronizedPool<>(MAX_STARTER_COUNT);

        DefaultFactory(ActivityManagerService service,
                ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
            mService = service;
@@ -234,9 +250,20 @@ class ActivityStarter {
        }

        @Override
        public ActivityStarter obtainStarter() {
            // TODO(b/64750076): Investigate recycling instances to reduce object creation overhead.
            return new ActivityStarter(mController, mService, mSupervisor, mInterceptor);
        public ActivityStarter obtain() {
            ActivityStarter starter = mStarterPool.acquire();

            if (starter == null) {
                starter = new ActivityStarter(mController, mService, mSupervisor, mInterceptor);
            }

            return starter;
        }

        @Override
        public void recycle(ActivityStarter starter) {
            starter.reset(true /* clearRequest*/);
            mStarterPool.release(starter);
        }
    }

@@ -289,6 +316,76 @@ class ActivityStarter {
         * {@see ActivityStarter#startActivityMayWait}.
         */
        boolean mayWait;

        /**
         * Sets values back to the initial state, clearing any held references.
         */
        void reset() {
            caller = null;
            intent = null;
            ephemeralIntent = null;
            resolvedType = null;
            activityInfo = null;
            resolveInfo = null;
            voiceSession = null;
            voiceInteractor = null;
            resultTo = null;
            resultWho = null;
            requestCode = 0;
            callingPid = 0;
            callingUid = 0;
            callingPackage = null;
            realCallingPid = 0;
            realCallingUid = 0;
            startFlags = 0;
            activityOptions = null;
            ignoreTargetSecurity = false;
            componentSpecified = false;
            outActivity = null;
            inTask = null;
            reason = null;
            profilerInfo = null;
            globalConfig = null;
            waitOptions = null;
            userId = 0;
            waitResult = null;
            mayWait = false;
        }

        /**
         * Adopts all values from passed in request.
         */
        void set(Request request) {
            caller = request.caller;
            intent = request.intent;
            ephemeralIntent = request.ephemeralIntent;
            resolvedType = request.resolvedType;
            activityInfo = request.activityInfo;
            resolveInfo = request.resolveInfo;
            voiceSession = request.voiceSession;
            voiceInteractor = request.voiceInteractor;
            resultTo = request.resultTo;
            resultWho = request.resultWho;
            requestCode = request.requestCode;
            callingPid = request.callingPid;
            callingUid = request.callingUid;
            callingPackage = request.callingPackage;
            realCallingPid = request.realCallingPid;
            realCallingUid = request.realCallingUid;
            startFlags = request.startFlags;
            activityOptions = request.activityOptions;
            ignoreTargetSecurity = request.ignoreTargetSecurity;
            componentSpecified = request.componentSpecified;
            outActivity = request.outActivity;
            inTask = request.inTask;
            reason = request.reason;
            profilerInfo = request.profilerInfo;
            globalConfig = request.globalConfig;
            waitOptions = request.waitOptions;
            userId = request.userId;
            waitResult = request.waitResult;
            mayWait = request.mayWait;
        }
    }

    ActivityStarter(ActivityStartController controller, ActivityManagerService service,
@@ -297,6 +394,52 @@ class ActivityStarter {
        mService = service;
        mSupervisor = supervisor;
        mInterceptor = interceptor;
        reset(true);
    }

    /**
     * Effectively duplicates the starter passed in. All state and request values will be
     * mirrored.
     * @param starter
     */
    void set(ActivityStarter starter) {
        mStartActivity = starter.mStartActivity;
        mIntent = starter.mIntent;
        mCallingUid = starter.mCallingUid;
        mOptions = starter.mOptions;

        mLaunchTaskBehind = starter.mLaunchTaskBehind;
        mLaunchFlags = starter.mLaunchFlags;
        mLaunchMode = starter.mLaunchMode;

        mLaunchBounds.set(starter.mLaunchBounds);

        mNotTop = starter.mNotTop;
        mDoResume = starter.mDoResume;
        mStartFlags = starter.mStartFlags;
        mSourceRecord = starter.mSourceRecord;
        mPreferredDisplayId = starter.mPreferredDisplayId;

        mInTask = starter.mInTask;
        mAddingToTask = starter.mAddingToTask;
        mReuseTask = starter.mReuseTask;

        mNewTaskInfo = starter.mNewTaskInfo;
        mNewTaskIntent = starter.mNewTaskIntent;
        mSourceStack = starter.mSourceStack;

        mTargetStack = starter.mTargetStack;
        mMovedToFront = starter.mMovedToFront;
        mNoAnimation = starter.mNoAnimation;
        mKeepCurTransition = starter.mKeepCurTransition;
        mAvoidMoveToFront = starter.mAvoidMoveToFront;

        mVoiceSession = starter.mVoiceSession;
        mVoiceInteractor = starter.mVoiceInteractor;

        mIntentDelivered = starter.mIntentDelivered;

        mRequest.set(starter.mRequest);
    }

    ActivityRecord getStartActivity() {
@@ -314,6 +457,7 @@ class ActivityStarter {
     * @return The starter result.
     */
    int execute() {
        try {
            // TODO(b/64750076): Look into passing request directly to these methods to allow
            // for transactional diffs and preprocessing.
            if (mRequest.mayWait) {
@@ -334,6 +478,27 @@ class ActivityStarter {
                        mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
                        mRequest.outActivity, mRequest.inTask, mRequest.reason);
            }
        } finally {
            onExecutionComplete();
        }
    }

    /**
     * Starts an activity based on the provided {@link ActivityRecord} and environment parameters.
     * Note that this method is called internally as well as part of {@link #startActivity}.
     *
     * @return The start result.
     */
    int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {
        try {
            return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                    doResume, options, inTask, outActivity);
        } finally {
            onExecutionComplete();
        }
    }

    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
@@ -370,6 +535,14 @@ class ActivityStarter {
        return result != START_ABORTED ? result : START_SUCCESS;
    }

    /**
     * Called when execution is complete. Sets state indicating completion and proceeds with
     * recycling if appropriate.
     */
    private void onExecutionComplete() {
        mController.onExecutionComplete(this);
    }

    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
@@ -652,7 +825,7 @@ class ActivityStarter {

        mController.doPendingActivityLaunches(false);

        return startResolvedActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
        return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                true /* doResume */, options, inTask, outActivity);
    }

@@ -925,13 +1098,7 @@ class ActivityStarter {
        }
    }

    /**
     * Starts an activity based on the provided {@link ActivityRecord} and environment parameters.
     * Note that this method is called internally as well as part of {@link #startActivity}.
     *
     * @return The start result.
     */
    int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord,
    private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
                ActivityRecord[] outActivity) {
@@ -1190,9 +1357,57 @@ class ActivityStarter {
        return START_SUCCESS;
    }

    /**
     * Resets the {@link ActivityStarter} state.
     * @param clearRequest whether the request should be reset to default values.
     */
    void reset(boolean clearRequest) {
        mStartActivity = null;
        mIntent = null;
        mCallingUid = -1;
        mOptions = null;

        mLaunchTaskBehind = false;
        mLaunchFlags = 0;
        mLaunchMode = INVALID_LAUNCH_MODE;

        mLaunchBounds.setEmpty();

        mNotTop = null;
        mDoResume = false;
        mStartFlags = 0;
        mSourceRecord = null;
        mPreferredDisplayId = INVALID_DISPLAY;

        mInTask = null;
        mAddingToTask = false;
        mReuseTask = null;

        mNewTaskInfo = null;
        mNewTaskIntent = null;
        mSourceStack = null;

        mTargetStack = null;
        mMovedToFront = false;
        mNoAnimation = false;
        mKeepCurTransition = false;
        mAvoidMoveToFront = false;

        mVoiceSession = null;
        mVoiceInteractor = null;

        mIntentDelivered = false;

        if (clearRequest) {
            mRequest.reset();
        }
    }

    private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
            boolean doResume, int startFlags, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
        reset(false /* clearRequest */);

        mStartActivity = r;
        mIntent = r.intent;
        mOptions = options;
+18 −2
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.am;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;

import android.content.Intent;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -61,14 +62,13 @@ public class ActivityStartControllerTests extends ActivityTestsBase {
        mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory);
        mStarter = spy(new ActivityStarter(mController, mService, mService.mStackSupervisor,
                mock(ActivityStartInterceptor.class)));
        doReturn(mStarter).when(mFactory).obtainStarter();
        doReturn(mStarter).when(mFactory).obtain();
    }

    /**
     * Ensures that pending launches are processed.
     */
    @Test
    @Presubmit
    public void testPendingActivityLaunches() {
        final Random random = new Random();

@@ -88,4 +88,20 @@ public class ActivityStartControllerTests extends ActivityTestsBase {
        verify(mStarter, times(1)).startResolvedActivity(eq(activity), eq(source), eq(null),
                eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null));
    }


    /**
     * Ensures instances are recycled after execution.
     */
    @Test
    public void testRecycling() throws Exception {
        final Intent intent = new Intent();
        final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
                mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
        optionStarter
                .setIntent(intent)
                .setReason("Test")
                .execute();
        verify(mFactory, times(1)).recycle(eq(optionStarter));
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ import static android.app.ActivityManager.START_PERMISSION_DENIED;
import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;

import com.android.internal.os.BatteryStatsImpl;
import com.android.server.am.ActivityStarter.Factory;

/**
 * Tests for the {@link ActivityStarter} class.