| package android.server.am.lifecycle; |
| |
| import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; |
| import static android.server.am.ActivityManagerState.STATE_PAUSED; |
| import static android.server.am.ActivityManagerState.STATE_STOPPED; |
| import static android.server.am.UiDeviceUtils.pressBackButton; |
| import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_ACTIVITY_RESULT; |
| import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_CREATE; |
| import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_DESTROY; |
| import static android.server.am.lifecycle.LifecycleLog.ActivityCallback |
| .ON_MULTI_WINDOW_MODE_CHANGED; |
| import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_NEW_INTENT; |
| import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_PAUSE; |
| import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_POST_CREATE; |
| import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_RESTART; |
| import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_RESUME; |
| import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_START; |
| import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_STOP; |
| import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.PRE_ON_CREATE; |
| import static android.view.Surface.ROTATION_0; |
| import static android.view.Surface.ROTATION_180; |
| import static android.view.Surface.ROTATION_270; |
| import static android.view.Surface.ROTATION_90; |
| |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.fail; |
| |
| import android.app.Activity; |
| import android.app.Instrumentation.ActivityMonitor; |
| import android.content.ComponentName; |
| import android.content.Intent; |
| import android.content.pm.ActivityInfo; |
| import android.platform.test.annotations.Presubmit; |
| import androidx.test.InstrumentationRegistry; |
| import androidx.test.filters.FlakyTest; |
| import androidx.test.filters.MediumTest; |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import com.android.compatibility.common.util.AmUtils; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.Arrays; |
| import java.util.List; |
| |
| /** |
| * Build/Install/Run: |
| * atest CtsActivityManagerDeviceTestCases:ActivityLifecycleTests |
| */ |
| @MediumTest |
| @RunWith(AndroidJUnit4.class) |
| @Presubmit |
| @FlakyTest(bugId = 77652261) |
| public class ActivityLifecycleTests extends ActivityLifecycleClientTestBase { |
| |
| @Test |
| public void testSingleLaunch() throws Exception { |
| final Activity activity = mFirstActivityTestRule.launchActivity(new Intent()); |
| waitAndAssertActivityStates(state(activity, ON_RESUME)); |
| |
| LifecycleVerifier.assertLaunchSequence(FirstActivity.class, getLifecycleLog(), |
| false /* includeCallbacks */); |
| } |
| |
| @Test |
| public void testLaunchOnTop() throws Exception { |
| final Activity firstActivity = mFirstActivityTestRule.launchActivity(new Intent()); |
| waitAndAssertActivityStates(state(firstActivity, ON_RESUME)); |
| |
| getLifecycleLog().clear(); |
| final Activity secondActivity = mSecondActivityTestRule.launchActivity(new Intent()); |
| waitAndAssertActivityStates(occludedActivityState(firstActivity, secondActivity), |
| state(secondActivity, ON_RESUME)); |
| |
| LifecycleVerifier.assertLaunchSequence(SecondActivity.class, FirstActivity.class, |
| getLifecycleLog(), isTranslucent(secondActivity)); |
| } |
| |
| @Test |
| public void testLaunchAndDestroy() throws Exception { |
| final Activity activity = mFirstActivityTestRule.launchActivity(new Intent()); |
| |
| activity.finish(); |
| waitAndAssertActivityStates(state(activity, ON_DESTROY)); |
| |
| LifecycleVerifier.assertLaunchAndDestroySequence(FirstActivity.class, getLifecycleLog()); |
| } |
| |
| @Test |
| public void testRelaunchResumed() throws Exception { |
| final Activity activity = mFirstActivityTestRule.launchActivity(new Intent()); |
| waitAndAssertActivityStates(state(activity, ON_RESUME)); |
| |
| getLifecycleLog().clear(); |
| InstrumentationRegistry.getInstrumentation().runOnMainSync(activity::recreate); |
| waitAndAssertActivityStates(state(activity, ON_RESUME)); |
| |
| LifecycleVerifier.assertRelaunchSequence(FirstActivity.class, getLifecycleLog(), ON_RESUME); |
| } |
| |
| @Test |
| public void testRelaunchPaused() throws Exception { |
| final Activity pausedActivity = mFirstActivityTestRule.launchActivity(new Intent()); |
| final Activity topTranslucentActivity = |
| mTranslucentActivityTestRule.launchActivity(new Intent()); |
| |
| waitAndAssertActivityStates(state(pausedActivity, ON_PAUSE), |
| state(topTranslucentActivity, ON_RESUME)); |
| |
| getLifecycleLog().clear(); |
| InstrumentationRegistry.getInstrumentation().runOnMainSync(pausedActivity::recreate); |
| waitAndAssertActivityStates(state(pausedActivity, ON_PAUSE)); |
| |
| LifecycleVerifier.assertRelaunchSequence(FirstActivity.class, getLifecycleLog(), ON_PAUSE); |
| } |
| |
| @Test |
| public void testRelaunchStopped() throws Exception { |
| final Activity stoppedActivity = mFirstActivityTestRule.launchActivity(new Intent()); |
| final Activity topActivity = mSecondActivityTestRule.launchActivity(new Intent()); |
| |
| waitAndAssertActivityStates( |
| occludedActivityState(stoppedActivity, topActivity), state(topActivity, ON_RESUME)); |
| |
| getLifecycleLog().clear(); |
| InstrumentationRegistry.getInstrumentation().runOnMainSync(stoppedActivity::recreate); |
| waitAndAssertActivityStates(occludedActivityState(stoppedActivity, topActivity)); |
| |
| LifecycleVerifier.assertRelaunchSequence(FirstActivity.class, getLifecycleLog(), |
| occludedActivityState(isTranslucent(topActivity))); |
| } |
| |
| @Test |
| public void testRelaunchConfigurationChangedWhileBecomingVisible() throws Exception { |
| if (!supportsRotation()) { |
| // Skip rotation test if device doesn't support it. |
| return; |
| } |
| |
| final Activity becomingVisibleActivity = |
| mFirstActivityTestRule.launchActivity(new Intent()); |
| final Activity translucentActivity = |
| mTranslucentActivityTestRule.launchActivity(new Intent()); |
| final Activity topOpaqueActivity = mSecondActivityTestRule.launchActivity(new Intent()); |
| |
| waitAndAssertActivityStates( |
| occludedActivityState(becomingVisibleActivity, topOpaqueActivity), |
| occludedActivityState(translucentActivity, topOpaqueActivity), |
| state(topOpaqueActivity, ON_RESUME)); |
| |
| try (final RotationSession rotationSession = new RotationSession()) { |
| if (!supportsLockedUserRotation( |
| rotationSession, translucentActivity.getDisplay().getDisplayId())) { |
| return; |
| } |
| |
| getLifecycleLog().clear(); |
| |
| final int current = rotationSession.get(); |
| // Set new rotation to cause a configuration change. |
| switch (current) { |
| case ROTATION_0: |
| case ROTATION_180: |
| rotationSession.set(ROTATION_90); |
| break; |
| case ROTATION_90: |
| case ROTATION_270: |
| rotationSession.set(ROTATION_0); |
| break; |
| default: |
| fail("Unknown rotation:" + current); |
| } |
| |
| // Assert that the top activity was relaunched. |
| waitAndAssertActivityStates(state(topOpaqueActivity, ON_RESUME)); |
| LifecycleVerifier.assertRelaunchSequence( |
| SecondActivity.class, getLifecycleLog(), ON_RESUME); |
| |
| // Finish the top activity |
| getLifecycleLog().clear(); |
| mSecondActivityTestRule.finishActivity(); |
| |
| // Assert that the translucent activity and the activity visible behind it were |
| // relaunched. |
| waitAndAssertActivityStates(state(becomingVisibleActivity, ON_PAUSE), |
| state(translucentActivity, ON_RESUME)); |
| |
| LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME, |
| ON_PAUSE), "becomingVisiblePaused"); |
| // TODO(b/77974794): New intent handling sequence should always be the same. |
| // It is possible to get an extra pause and resume now. |
| final List<LifecycleLog.ActivityCallback> expectedSequence = |
| Arrays.asList(ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME); |
| final List<LifecycleLog.ActivityCallback> extraCycleSequence = |
| Arrays.asList(ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME, |
| ON_PAUSE, ON_RESUME); |
| LifecycleVerifier.assertSequenceMatchesOneOf(TranslucentActivity.class, |
| getLifecycleLog(), |
| Arrays.asList(expectedSequence, extraCycleSequence), |
| "becomingVisibleResumed"); |
| } |
| } |
| |
| @Test |
| public void testPausedWithTranslucentOnTop() throws Exception { |
| // Launch fullscreen activity |
| final Activity firstActivity = |
| mFirstActivityTestRule.launchActivity(new Intent()); |
| |
| // Launch translucent activity on top |
| mTranslucentActivityTestRule.launchActivity(new Intent()); |
| |
| // Launch another translucent activity on top to make sure the fullscreen activity |
| // transitions to final state |
| final Activity secondTranslucentActivity = |
| mSecondTranslucentActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for the second translucent activity to become resumed. |
| waitAndAssertActivityStates(state(secondTranslucentActivity, ON_RESUME), |
| state(firstActivity, ON_PAUSE)); |
| |
| // Assert that the fullscreen activity was not stopped and is in the paused state. |
| LifecycleVerifier.assertLaunchAndPauseSequence(FirstActivity.class, getLifecycleLog()); |
| } |
| |
| @Test |
| public void testPausedWhenReturningWithTranslucentOnTop() throws Exception { |
| // Launch fullscreen activity |
| final Activity firstActivity = |
| mFirstActivityTestRule.launchActivity(new Intent()); |
| |
| // Launch translucent activity |
| final Activity translucentActivity = |
| mTranslucentActivityTestRule.launchActivity(new Intent()); |
| |
| // Launch another fullscreen activity |
| final Activity secondActivity = |
| mSecondActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for top activity to resume |
| waitAndAssertActivityStates(state(secondActivity, ON_RESUME), |
| occludedActivityState(translucentActivity, secondActivity), |
| occludedActivityState(firstActivity, secondActivity)); |
| |
| getLifecycleLog().clear(); |
| |
| final boolean secondActivityIsTranslucent = ActivityInfo.isTranslucentOrFloating( |
| secondActivity.getWindow().getWindowStyle()); |
| |
| // Finish top activity |
| mSecondActivityTestRule.finishActivity(); |
| |
| if (secondActivityIsTranslucent) { |
| // In this case we don't expect the state of the firstActivity to change since it is |
| // already in the visible paused state. So, we just verify that translucentActivity |
| // transitions to resumed state. |
| waitAndAssertActivityStates(state(translucentActivity, ON_RESUME)); |
| } else { |
| // Wait for translucent activity to resume |
| waitAndAssertActivityStates(state(translucentActivity, ON_RESUME), |
| state(firstActivity, ON_START)); |
| |
| // Verify that the first activity was restarted |
| LifecycleVerifier.assertRestartSequence(FirstActivity.class, getLifecycleLog()); |
| } |
| } |
| |
| @Test |
| public void testPausedWhenRecreatedFromInNonFocusedStack() throws Exception { |
| if (!supportsSplitScreenMultiWindow()) { |
| // Skipping test: no split multi-window support |
| return; |
| } |
| |
| // Launch first activity |
| final Activity firstActivity = |
| mFirstActivityTestRule.launchActivity(new Intent()); |
| |
| // Launch second activity to stop first |
| final Activity secondActivity = |
| mSecondActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for second activity to resume. We must also wait for the first activity to stop |
| // so that this event is not included in the logs. |
| waitAndAssertActivityStates(state(secondActivity, ON_RESUME), |
| state(firstActivity, ON_STOP)); |
| |
| // Enter split screen |
| moveTaskToPrimarySplitScreen(secondActivity.getTaskId()); |
| |
| // CLear logs so we can capture just the destroy sequence |
| getLifecycleLog().clear(); |
| |
| // Start an activity in separate task (will be placed in secondary stack) |
| getLaunchActivityBuilder().execute(); |
| |
| // Finish top activity |
| secondActivity.finish(); |
| |
| waitAndAssertActivityStates(state(firstActivity, ON_PAUSE)); |
| |
| // Verify that the first activity was recreated to pause as it was created before |
| // windowing mode was switched |
| LifecycleVerifier.assertRecreateAndPauseSequence(FirstActivity.class, getLifecycleLog()); |
| } |
| |
| @Test |
| public void testResultInNonFocusedStack() throws Exception { |
| if (!supportsSplitScreenMultiWindow()) { |
| // Skipping test: no split multi-window support |
| return; |
| } |
| |
| // Launch first activity |
| final Activity callbackTrackingActivity = |
| mCallbackTrackingActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for first activity to resume |
| waitAndAssertActivityStates(state(callbackTrackingActivity, ON_RESUME)); |
| |
| // Enter split screen |
| moveTaskToPrimarySplitScreen(callbackTrackingActivity.getTaskId(), |
| true /* launchSideActivityIfNeeded */); |
| |
| // Launch second activity to pause first |
| // Create an ActivityMonitor that catch ChildActivity and return mock ActivityResult: |
| ActivityMonitor activityMonitor = InstrumentationRegistry.getInstrumentation() |
| .addMonitor(SecondActivity.class.getName(), null /* activityResult */, |
| false /* block */); |
| |
| callbackTrackingActivity.startActivityForResult( |
| new Intent(callbackTrackingActivity, SecondActivity.class), 1 /* requestCode */); |
| |
| // Wait for the ActivityMonitor to be hit |
| final Activity secondActivity = InstrumentationRegistry.getInstrumentation() |
| .waitForMonitorWithTimeout(activityMonitor, 5 * 1000); |
| |
| // Wait for second activity to resume |
| assertNotNull("Second activity should be started", secondActivity); |
| waitAndAssertActivityStates(state(secondActivity, ON_RESUME)); |
| |
| // Start an activity in separate task (will be placed in secondary stack) |
| getLaunchActivityBuilder().execute(); |
| |
| waitAndAssertActivityStates(state(secondActivity, ON_PAUSE)); |
| waitAndAssertActivityStates(state(callbackTrackingActivity, ON_STOP)); |
| |
| // Finish top activity and verify that activity below became focused. |
| getLifecycleLog().clear(); |
| secondActivity.setResult(Activity.RESULT_OK); |
| secondActivity.finish(); |
| |
| waitAndAssertActivityStates(state(callbackTrackingActivity, ON_START)); |
| LifecycleVerifier.assertRestartSequence(CallbackTrackingActivity.class, getLifecycleLog()); |
| |
| // Bring the first activity to front to verify that it receives the result. |
| getLifecycleLog().clear(); |
| final Intent singleTopIntent = new Intent(InstrumentationRegistry.getTargetContext(), |
| CallbackTrackingActivity.class); |
| singleTopIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); |
| InstrumentationRegistry.getTargetContext().startActivity(singleTopIntent); |
| |
| waitAndAssertActivityStates(state(callbackTrackingActivity, ON_RESUME)); |
| LifecycleVerifier.assertSequence(CallbackTrackingActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_ACTIVITY_RESULT, ON_NEW_INTENT, ON_RESUME), "bring to front"); |
| } |
| |
| @Test |
| public void testPausedWhenRestartedFromInNonFocusedStack() throws Exception { |
| if (!supportsSplitScreenMultiWindow()) { |
| // Skipping test: no split multi-window support |
| return; |
| } |
| |
| // Launch first activity |
| final Activity firstActivity = |
| mFirstActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for first activity to resume |
| waitAndAssertActivityStates(state(firstActivity, ON_RESUME)); |
| |
| // Enter split screen |
| moveTaskToPrimarySplitScreen(firstActivity.getTaskId(), |
| true /* launchSideActivityIfNeeded */); |
| |
| // Launch second activity to pause first |
| final Activity secondActivity = |
| mSecondActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for second activity to resume |
| waitAndAssertActivityStates(state(secondActivity, ON_RESUME)); |
| |
| // Start an activity in separate task (will be placed in secondary stack) |
| getLaunchActivityBuilder().execute(); |
| |
| waitAndAssertActivityStates(state(secondActivity, ON_PAUSE)); |
| |
| getLifecycleLog().clear(); |
| |
| // Finish top activity |
| secondActivity.finish(); |
| |
| waitAndAssertActivityStates(state(firstActivity, ON_PAUSE)); |
| |
| // Verify that the first activity was restarted to pause as it was brought back after |
| // windowing mode was switched |
| LifecycleVerifier.assertRestartAndPauseSequence(FirstActivity.class, getLifecycleLog()); |
| } |
| |
| @Test |
| public void testOnActivityResult() throws Exception { |
| final Intent intent = new Intent(); |
| intent.putExtra(EXTRA_FINISH_IN_ON_RESUME, true); |
| mLaunchForResultActivityTestRule.launchActivity(intent); |
| |
| final List<LifecycleLog.ActivityCallback> expectedSequence = |
| Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_PAUSE, ON_ACTIVITY_RESULT, ON_RESUME); |
| waitForActivityTransitions(LaunchForResultActivity.class, expectedSequence); |
| |
| // TODO(b/79218023): First activity might also be stopped before getting result. |
| final List<LifecycleLog.ActivityCallback> sequenceWithStop = |
| Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_PAUSE, ON_STOP, ON_ACTIVITY_RESULT, ON_RESTART, ON_START, ON_RESUME); |
| LifecycleVerifier.assertSequenceMatchesOneOf(LaunchForResultActivity.class, |
| getLifecycleLog(), Arrays.asList(expectedSequence, sequenceWithStop), |
| "activityResult"); |
| } |
| |
| @Test |
| public void testOnActivityResultAfterStop() throws Exception { |
| final Intent intent = new Intent(); |
| intent.putExtra(EXTRA_FINISH_AFTER_RESUME, true); |
| mLaunchForResultActivityTestRule.launchActivity(intent); |
| final boolean isTranslucent = isTranslucent(mLaunchForResultActivityTestRule.getActivity()); |
| |
| final List<LifecycleLog.ActivityCallback> expectedSequence; |
| if (isTranslucent) { |
| expectedSequence = Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, |
| ON_RESUME, ON_PAUSE, ON_ACTIVITY_RESULT, ON_RESUME); |
| } else { |
| expectedSequence = Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, |
| ON_RESUME, ON_PAUSE, ON_STOP, ON_ACTIVITY_RESULT, ON_RESTART, ON_START, |
| ON_RESUME); |
| } |
| waitForActivityTransitions(LaunchForResultActivity.class, expectedSequence); |
| |
| LifecycleVerifier.assertSequence(LaunchForResultActivity.class, |
| getLifecycleLog(), expectedSequence, "activityResult"); |
| } |
| |
| @Test |
| public void testOnPostCreateAfterCreate() throws Exception { |
| final Activity callbackTrackingActivity = |
| mCallbackTrackingActivityTestRule.launchActivity(new Intent()); |
| |
| waitAndAssertActivityStates(state(callbackTrackingActivity, ON_RESUME)); |
| |
| LifecycleVerifier.assertSequence(CallbackTrackingActivity.class, |
| getLifecycleLog(), |
| Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME), |
| "create"); |
| } |
| |
| @Test |
| public void testOnPostCreateAfterRecreateInOnResume() throws Exception { |
| // Launch activity |
| final Activity trackingActivity = |
| mCallbackTrackingActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for activity to resume |
| waitAndAssertActivityStates(state(trackingActivity, ON_RESUME)); |
| |
| // Call "recreate" and assert sequence |
| getLifecycleLog().clear(); |
| InstrumentationRegistry.getInstrumentation().runOnMainSync(trackingActivity::recreate); |
| waitAndAssertActivityStates(state(trackingActivity, ON_RESUME)); |
| |
| LifecycleVerifier.assertSequence(CallbackTrackingActivity.class, |
| getLifecycleLog(), |
| Arrays.asList(ON_PAUSE, ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, |
| ON_POST_CREATE, ON_RESUME), |
| "recreate"); |
| } |
| |
| @Test |
| public void testOnPostCreateAfterRecreateInOnPause() throws Exception { |
| // Launch activity |
| final Activity trackingActivity = |
| mCallbackTrackingActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for activity to resume |
| waitAndAssertActivityStates(state(trackingActivity, ON_RESUME)); |
| |
| // Launch translucent activity, which will make the first one paused. |
| mTranslucentActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for first activity to become paused |
| waitAndAssertActivityStates(state(trackingActivity, ON_PAUSE)); |
| |
| // Call "recreate" and assert sequence |
| getLifecycleLog().clear(); |
| InstrumentationRegistry.getInstrumentation().runOnMainSync(trackingActivity::recreate); |
| waitAndAssertActivityStates(state(trackingActivity, ON_PAUSE)); |
| |
| LifecycleVerifier.assertSequence(CallbackTrackingActivity.class, |
| getLifecycleLog(), |
| Arrays.asList(ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, |
| ON_POST_CREATE, ON_RESUME, ON_PAUSE), |
| "recreate"); |
| } |
| |
| @Test |
| public void testOnPostCreateAfterRecreateInOnStop() throws Exception { |
| // Launch first activity |
| final Activity trackingActivity = |
| mCallbackTrackingActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for activity to resume |
| waitAndAssertActivityStates(state(trackingActivity, ON_RESUME)); |
| |
| // Launch second activity to cover and stop first |
| final Activity secondActivity = |
| mSecondActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for second activity to become resumed |
| waitAndAssertActivityStates(state(secondActivity, ON_RESUME)); |
| |
| // Wait for first activity to become stopped |
| waitAndAssertActivityStates(occludedActivityState(trackingActivity, secondActivity)); |
| |
| // Call "recreate" and assert sequence |
| getLifecycleLog().clear(); |
| InstrumentationRegistry.getInstrumentation().runOnMainSync(trackingActivity::recreate); |
| waitAndAssertActivityStates(occludedActivityState(trackingActivity, secondActivity)); |
| |
| final List<LifecycleLog.ActivityCallback> callbacks; |
| if (isTranslucent(secondActivity)) { |
| callbacks = Arrays.asList(ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, |
| ON_POST_CREATE, ON_RESUME, ON_PAUSE); |
| } else { |
| callbacks = Arrays.asList(ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, |
| ON_POST_CREATE, ON_RESUME, ON_PAUSE, ON_STOP); |
| } |
| |
| LifecycleVerifier.assertSequence( |
| CallbackTrackingActivity.class, getLifecycleLog(), callbacks, "recreate"); |
| } |
| |
| /** |
| * The following test ensures an activity is brought back if its process is ended in the |
| * background. |
| */ |
| @Test |
| public void testRestoreFromKill() throws Exception { |
| final LaunchActivityBuilder builder = getLaunchActivityBuilder(); |
| final ComponentName targetActivity = builder.getTargetActivity(); |
| |
| // Launch activity whose process will be killed |
| builder.execute(); |
| |
| // Start activity in another process to put original activity in background. |
| mFirstActivityTestRule.launchActivity(new Intent()); |
| final boolean isTranslucent = isTranslucent(mFirstActivityTestRule.getActivity()); |
| mAmWmState.waitForActivityState( |
| targetActivity, isTranslucent ? STATE_PAUSED : STATE_STOPPED); |
| |
| // Only try to kill targetActivity if the top activity isn't translucent. If the top |
| // activity is translucent then targetActivity will be visible, so the process will be |
| // started again really quickly. |
| if (!isTranslucent) { |
| // Kill first activity |
| AmUtils.runKill(targetActivity.getPackageName(), true /* wait */); |
| } |
| |
| // Return back to first activity |
| pressBackButton(); |
| |
| // Verify activity is resumed |
| mAmWmState.waitForValidState(targetActivity); |
| mAmWmState.assertResumedActivity("Originally launched activity should be resumed", |
| targetActivity); |
| } |
| |
| /** |
| * The that recreate request from an activity is executed immediately. |
| */ |
| @Test |
| public void testLocalRecreate() throws Exception { |
| // Launch the activity that will recreate itself |
| Activity recreatingActivity = mSingleTopActivityTestRule.launchActivity(new Intent()); |
| |
| // Launch second activity to cover and stop first |
| final LaunchActivityBuilder launchActivityBuilder = getLaunchActivityBuilder(); |
| launchActivityBuilder.setNewTask(true).setMultipleTask(true).execute(); |
| |
| // Wait for first activity to become stopped |
| waitAndAssertActivityStates(occludedActivityState(recreatingActivity, |
| launchActivityBuilder.isTargetActivityTranslucent())); |
| |
| // Launch the activity again to recreate |
| getLifecycleLog().clear(); |
| final Intent intent = new Intent(InstrumentationRegistry.getContext(), |
| SingleTopActivity.class); |
| intent.putExtra(EXTRA_RECREATE, true); |
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| InstrumentationRegistry.getTargetContext().startActivity(intent); |
| |
| // Wait for activity to relaunch and resume |
| final List<LifecycleLog.ActivityCallback> expectedRelaunchSequence; |
| if (launchActivityBuilder.isTargetActivityTranslucent()) { |
| expectedRelaunchSequence = Arrays.asList(ON_NEW_INTENT, ON_RESUME, ON_PAUSE, ON_STOP, |
| ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_PAUSE, ON_RESUME); |
| } else { |
| expectedRelaunchSequence = Arrays.asList(ON_NEW_INTENT, ON_RESTART, ON_START, ON_RESUME, |
| ON_PAUSE, ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, |
| ON_POST_CREATE, ON_RESUME); |
| } |
| |
| waitForActivityTransitions(SingleTopActivity.class, expectedRelaunchSequence); |
| LifecycleVerifier.assertSequence(SingleTopActivity.class, getLifecycleLog(), |
| expectedRelaunchSequence, "recreate"); |
| } |
| |
| @Test |
| public void testOnNewIntent() throws Exception { |
| // Launch a singleTop activity |
| final Activity singleTopActivity = |
| mSingleTopActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for the activity to resume |
| waitAndAssertActivityStates(state(singleTopActivity, ON_RESUME)); |
| LifecycleVerifier.assertLaunchSequence(SingleTopActivity.class, getLifecycleLog(), |
| true /* includeCallbacks */); |
| |
| // Try to launch again |
| getLifecycleLog().clear(); |
| final Intent intent = new Intent(InstrumentationRegistry.getContext(), |
| SingleTopActivity.class); |
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| InstrumentationRegistry.getTargetContext().startActivity(intent); |
| |
| // Wait for the activity to resume again |
| waitAndAssertActivityStates(state(singleTopActivity, ON_RESUME)); |
| |
| // Verify that the first activity was paused, new intent was delivered and resumed again |
| LifecycleVerifier.assertSequence(SingleTopActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_PAUSE, ON_NEW_INTENT, ON_RESUME), "newIntent"); |
| } |
| |
| @Test |
| public void testOnNewIntentFromHidden() throws Exception { |
| // Launch a singleTop activity |
| final Activity singleTopActivity = |
| mSingleTopActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for the activity to resume |
| waitAndAssertActivityStates(state(singleTopActivity, ON_RESUME)); |
| LifecycleVerifier.assertLaunchSequence(SingleTopActivity.class, getLifecycleLog(), |
| true /* includeCallbacks */); |
| |
| // Launch something on top |
| final Intent newTaskIntent = new Intent(); |
| newTaskIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); |
| final Activity secondActivity = mSecondActivityTestRule.launchActivity(newTaskIntent); |
| |
| // Wait for the activity to resume |
| waitAndAssertActivityStates(state(secondActivity, ON_RESUME)); |
| waitAndAssertActivityStates(occludedActivityState(singleTopActivity, secondActivity)); |
| |
| // Try to launch again |
| getLifecycleLog().clear(); |
| final Intent intent = new Intent(InstrumentationRegistry.getContext(), |
| SingleTopActivity.class); |
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| InstrumentationRegistry.getTargetContext().startActivity(intent); |
| |
| // Wait for the activity to resume again |
| waitAndAssertActivityStates(state(singleTopActivity, ON_RESUME)); |
| |
| // Verify that the first activity was restarted, new intent was delivered and resumed again |
| final List<LifecycleLog.ActivityCallback> expectedSequence; |
| final List<LifecycleLog.ActivityCallback> otherSequence; |
| if (isTranslucent(singleTopActivity)) { |
| expectedSequence = Arrays.asList(ON_NEW_INTENT, ON_RESUME, ON_PAUSE, ON_RESUME); |
| otherSequence = Arrays.asList(ON_RESTART, ON_START, ON_NEW_INTENT, ON_RESUME); |
| } else { |
| expectedSequence = Arrays.asList(ON_NEW_INTENT, ON_RESTART, ON_START, ON_RESUME); |
| otherSequence = Arrays.asList(ON_RESTART, ON_START, ON_NEW_INTENT, ON_RESUME); |
| } |
| // TODO(b/65236456): This should always be ON_RESTART, ON_START, ON_NEW_INTENT, ON_RESUME |
| LifecycleVerifier.assertSequenceMatchesOneOf(SingleTopActivity.class, getLifecycleLog(), |
| Arrays.asList(expectedSequence, otherSequence), "newIntent"); |
| } |
| |
| @Test |
| public void testOnNewIntentFromPaused() throws Exception { |
| // Launch a singleTop activity |
| final Activity singleTopActivity = |
| mSingleTopActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for the activity to resume |
| waitAndAssertActivityStates(state(singleTopActivity, ON_RESUME)); |
| LifecycleVerifier.assertLaunchSequence(SingleTopActivity.class, getLifecycleLog(), |
| true /* includeCallbacks */); |
| |
| // Launch translucent activity, which will make the first one paused. |
| mTranslucentActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for the activity to pause |
| waitAndAssertActivityStates(state(singleTopActivity, ON_PAUSE)); |
| |
| // Try to launch again |
| getLifecycleLog().clear(); |
| final Intent intent = new Intent(InstrumentationRegistry.getContext(), |
| SingleTopActivity.class); |
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); |
| InstrumentationRegistry.getTargetContext().startActivity(intent); |
| |
| // Wait for the activity to resume again |
| // TODO(b/77974794): New intent handling sequence should always be the same. |
| // It is possible to get an extra pause and resume now. |
| final List<LifecycleLog.ActivityCallback> extraPauseSequence = |
| Arrays.asList(ON_NEW_INTENT, ON_RESUME, ON_PAUSE, ON_RESUME); |
| waitForActivityTransitions(SingleTopActivity.class, extraPauseSequence); |
| |
| // Verify that the new intent was delivered and resumed again |
| final List<LifecycleLog.ActivityCallback> expectedSequence = |
| Arrays.asList(ON_NEW_INTENT, ON_RESUME); |
| LifecycleVerifier.assertSequenceMatchesOneOf(SingleTopActivity.class, getLifecycleLog(), |
| Arrays.asList(expectedSequence, extraPauseSequence), "newIntent"); |
| } |
| |
| @Test |
| public void testLifecycleOnMoveToFromSplitScreenRelaunch() throws Exception { |
| if (!supportsSplitScreenMultiWindow()) { |
| // Skipping test: no split multi-window support |
| return; |
| } |
| |
| // Launch a singleTop activity |
| final Activity testActivity = |
| mCallbackTrackingActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for the activity to resume |
| waitAndAssertActivityStates(state(testActivity, ON_RESUME)); |
| LifecycleVerifier.assertLaunchSequence(CallbackTrackingActivity.class, getLifecycleLog(), |
| true /* includeCallbacks */); |
| |
| // Enter split screen |
| getLifecycleLog().clear(); |
| setActivityTaskWindowingMode(CALLBACK_TRACKING_ACTIVITY, |
| WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); |
| |
| // Wait for the activity to pause |
| final List<LifecycleLog.ActivityCallback> expectedEnterSequence = |
| Arrays.asList(ON_PAUSE, ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, |
| ON_POST_CREATE, ON_RESUME, ON_MULTI_WINDOW_MODE_CHANGED, ON_PAUSE); |
| waitForActivityTransitions(CallbackTrackingActivity.class, expectedEnterSequence); |
| |
| // Verify that the activity was relaunched and received multi-window mode change |
| LifecycleVerifier.assertSequence(CallbackTrackingActivity.class, getLifecycleLog(), |
| expectedEnterSequence, "moveToSplitScreen"); |
| |
| // Exit split-screen |
| getLifecycleLog().clear(); |
| setActivityTaskWindowingMode(CALLBACK_TRACKING_ACTIVITY, WINDOWING_MODE_FULLSCREEN); |
| |
| // Wait for the activity to resume |
| final List<LifecycleLog.ActivityCallback> expectedExitSequence = |
| Arrays.asList(ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, |
| ON_POST_CREATE, ON_RESUME, ON_PAUSE, ON_MULTI_WINDOW_MODE_CHANGED, |
| ON_RESUME); |
| waitForActivityTransitions(CallbackTrackingActivity.class, expectedExitSequence); |
| |
| // Verify that the activity was relaunched and received multi-window mode change |
| LifecycleVerifier.assertSequence(CallbackTrackingActivity.class, getLifecycleLog(), |
| expectedExitSequence, "moveFromSplitScreen"); |
| } |
| |
| @Test |
| public void testLifecycleOnMoveToFromSplitScreenNoRelaunch() throws Exception { |
| if (!supportsSplitScreenMultiWindow()) { |
| // Skipping test: no split multi-window support |
| return; |
| } |
| |
| // Launch a singleTop activity |
| final Activity testActivity = |
| mConfigChangeHandlingActivityTestRule.launchActivity(new Intent()); |
| |
| // Wait for the activity to resume |
| waitAndAssertActivityStates(state(testActivity, ON_RESUME)); |
| LifecycleVerifier.assertLaunchSequence(ConfigChangeHandlingActivity.class, |
| getLifecycleLog(), true /* includeCallbacks */); |
| |
| // Enter split screen |
| getLifecycleLog().clear(); |
| setActivityTaskWindowingMode(CONFIG_CHANGE_HANDLING_ACTIVITY, |
| WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); |
| |
| // Wait for the activity to pause |
| waitAndAssertActivityStates(state(testActivity, ON_PAUSE)); |
| |
| // Verify that the activity was relaunched and received multi-window mode change |
| LifecycleVerifier.assertSequence(ConfigChangeHandlingActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_MULTI_WINDOW_MODE_CHANGED, ON_PAUSE), "moveToSplitScreen"); |
| |
| // Exit split-screen |
| getLifecycleLog().clear(); |
| setActivityTaskWindowingMode(CONFIG_CHANGE_HANDLING_ACTIVITY, WINDOWING_MODE_FULLSCREEN); |
| |
| // Wait for the activity to resume |
| waitAndAssertActivityStates(state(testActivity, ON_RESUME)); |
| |
| // Verify that the activity was relaunched and received multi-window mode change |
| LifecycleVerifier.assertSequence(ConfigChangeHandlingActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_MULTI_WINDOW_MODE_CHANGED, ON_RESUME), "moveFromSplitScreen"); |
| } |
| } |