Loading services/core/java/com/android/server/am/ActivityStartController.java +15 −4 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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); } /** Loading @@ -142,6 +149,10 @@ public class ActivityStartController { */ void postStartActivityProcessingForLastStarter(ActivityRecord r, int result, ActivityStack targetStack) { if (mLastStarter == null) { return; } mLastStarter.postStartActivityProcessing(r, result, targetStack); } Loading services/core/java/com/android/server/am/ActivityStarter.java +250 −35 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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; Loading @@ -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); } } Loading Loading @@ -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, Loading @@ -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() { Loading @@ -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) { Loading @@ -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, Loading Loading @@ -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, Loading Loading @@ -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); } Loading Loading @@ -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) { Loading Loading @@ -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; Loading services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java +18 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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)); } } services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java +1 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading
services/core/java/com/android/server/am/ActivityStartController.java +15 −4 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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); } /** Loading @@ -142,6 +149,10 @@ public class ActivityStartController { */ void postStartActivityProcessingForLastStarter(ActivityRecord r, int result, ActivityStack targetStack) { if (mLastStarter == null) { return; } mLastStarter.postStartActivityProcessing(r, result, targetStack); } Loading
services/core/java/com/android/server/am/ActivityStarter.java +250 −35 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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; Loading @@ -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); } } Loading Loading @@ -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, Loading @@ -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() { Loading @@ -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) { Loading @@ -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, Loading Loading @@ -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, Loading Loading @@ -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); } Loading Loading @@ -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) { Loading Loading @@ -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; Loading
services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java +18 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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)); } }
services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java +1 −0 Original line number Diff line number Diff line Loading @@ -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. Loading