| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package android.server.am.lifecycle; |
| |
| import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; |
| import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; |
| 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_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.ON_TOP_POSITION_GAINED; |
| import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_LOST; |
| 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 androidx.test.InstrumentationRegistry.getInstrumentation; |
| |
| import static org.junit.Assert.fail; |
| |
| import android.app.Activity; |
| import android.content.ComponentName; |
| import android.content.Intent; |
| import android.content.pm.ActivityInfo; |
| import android.platform.test.annotations.Presubmit; |
| |
| import androidx.test.filters.FlakyTest; |
| import androidx.test.filters.MediumTest; |
| import androidx.test.rule.ActivityTestRule; |
| |
| import com.android.compatibility.common.util.AmUtils; |
| |
| import org.junit.Test; |
| |
| import java.util.Arrays; |
| import java.util.List; |
| |
| /** |
| * Build/Install/Run: |
| * atest CtsActivityManagerDeviceTestCases:ActivityLifecycleTests |
| */ |
| @FlakyTest(bugId = 77652261) |
| @MediumTest |
| @Presubmit |
| 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()); |
| } |
| |
| @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 testLaunchTranslucentOnTop() throws Exception { |
| // Launch fullscreen activity |
| final Activity firstActivity = mFirstActivityTestRule.launchActivity(new Intent()); |
| waitAndAssertActivityStates(state(firstActivity, ON_RESUME)); |
| |
| // Launch translucent activity on top |
| getLifecycleLog().clear(); |
| final Activity translucentActivity = |
| mTranslucentActivityTestRule.launchActivity(new Intent()); |
| waitAndAssertActivityStates(state(firstActivity, ON_PAUSE), |
| state(translucentActivity, ON_RESUME)); |
| |
| LifecycleVerifier.assertLaunchSequence(TranslucentActivity.class, FirstActivity.class, |
| getLifecycleLog(), true /* launchIsTranslucent */); |
| } |
| |
| @Test |
| public void testLaunchDoubleTranslucentOnTop() throws Exception { |
| final Activity firstActivity = mFirstActivityTestRule.launchActivity(new Intent()); |
| waitAndAssertActivityStates(state(firstActivity, ON_RESUME)); |
| |
| // Launch translucent activity on top |
| getLifecycleLog().clear(); |
| final Activity translucentActivity = |
| mTranslucentActivityTestRule.launchActivity(new Intent()); |
| waitAndAssertActivityStates(state(firstActivity, ON_PAUSE), |
| state(translucentActivity, ON_RESUME)); |
| |
| LifecycleVerifier.assertLaunchSequence(TranslucentActivity.class, FirstActivity.class, |
| getLifecycleLog(), true /* launchIsTranslucent */); |
| |
| // Launch another translucent activity on top |
| getLifecycleLog().clear(); |
| final Activity secondTranslucentActivity = |
| mSecondTranslucentActivityTestRule.launchActivity(new Intent()); |
| waitAndAssertActivityStates(state(translucentActivity, ON_PAUSE), |
| state(secondTranslucentActivity, ON_RESUME)); |
| LifecycleVerifier.assertSequence(TranslucentActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_PAUSE), "launch"); |
| LifecycleVerifier.assertEmptySequence(FirstActivity.class, getLifecycleLog(), "launch"); |
| |
| // Finish top translucent activity |
| getLifecycleLog().clear(); |
| secondTranslucentActivity.finish(); |
| |
| waitAndAssertActivityStates(state(translucentActivity, ON_RESUME)); |
| waitAndAssertActivityStates(state(secondTranslucentActivity, ON_DESTROY)); |
| LifecycleVerifier.assertResumeToDestroySequence(SecondTranslucentActivity.class, |
| getLifecycleLog()); |
| LifecycleVerifier.assertSequence(TranslucentActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_RESUME), "launch"); |
| LifecycleVerifier.assertEmptySequence(FirstActivity.class, getLifecycleLog(), "launch"); |
| } |
| |
| @Test |
| public void testTranslucentMovedIntoStack() throws Exception { |
| // Launch a translucent activity and a regular activity in separate stacks |
| final Activity translucentActivity = |
| mTranslucentActivityTestRule.launchActivity(new Intent()); |
| final Activity firstActivity = mFirstActivityTestRule.launchActivity( |
| new Intent().setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)); |
| waitAndAssertActivityStates(state(firstActivity, ON_RESUME), |
| state(translucentActivity, ON_STOP)); |
| |
| final ComponentName firstActivityName = getComponentName(FirstActivity.class); |
| mAmWmState.computeState(firstActivityName); |
| int firstActivityStack = mAmWmState.getAmState().getStackIdByActivity(firstActivityName); |
| |
| // Move translucent activity into the stack with the first activity |
| getLifecycleLog().clear(); |
| moveActivityToStack(getComponentName(TranslucentActivity.class), firstActivityStack); |
| |
| // Wait for translucent activity to resume and first activity to pause |
| waitAndAssertActivityStates(state(translucentActivity, ON_RESUME), |
| state(firstActivity, ON_PAUSE)); |
| LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_PAUSE), "launchOnTop"); |
| LifecycleVerifier.assertRestartAndResumeSequence(TranslucentActivity.class, |
| getLifecycleLog()); |
| } |
| |
| @Test |
| public void testDestroyTopTranslucent() throws Exception { |
| // Launch a regular activity and a a translucent activity in the same stack |
| final Activity firstActivity = mFirstActivityTestRule.launchActivity(new Intent()); |
| final Activity translucentActivity = |
| mTranslucentActivityTestRule.launchActivity(new Intent()); |
| waitAndAssertActivityStates(state(firstActivity, ON_PAUSE), |
| state(translucentActivity, ON_RESUME)); |
| |
| // Finish translucent activity |
| getLifecycleLog().clear(); |
| mTranslucentActivityTestRule.finishActivity(); |
| |
| waitAndAssertActivityStates(state(firstActivity, ON_RESUME), |
| state(translucentActivity, ON_DESTROY)); |
| |
| // Verify destruction lifecycle |
| LifecycleVerifier.assertResumeToDestroySequence(TranslucentActivity.class, |
| getLifecycleLog()); |
| LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_RESUME), "resumeAfterTopDestroyed"); |
| } |
| |
| @Test |
| public void testDestroyOnTopOfTranslucent() 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(); |
| |
| waitAndAssertActivityStates(state(secondActivity, ON_DESTROY)); |
| LifecycleVerifier.assertResumeToDestroySequence(SecondActivity.class, getLifecycleLog()); |
| 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 testDestroyDoubleTranslucentOnTop() throws Exception { |
| final Activity firstActivity = mFirstActivityTestRule.launchActivity(new Intent()); |
| final Activity translucentActivity = |
| mTranslucentActivityTestRule.launchActivity(new Intent()); |
| final Activity secondTranslucentActivity = |
| mSecondTranslucentActivityTestRule.launchActivity(new Intent()); |
| waitAndAssertActivityStates(state(firstActivity, ON_PAUSE), |
| state(translucentActivity, ON_PAUSE), state(secondTranslucentActivity, ON_RESUME)); |
| |
| // Finish top translucent activity |
| getLifecycleLog().clear(); |
| secondTranslucentActivity.finish(); |
| |
| waitAndAssertActivityStates(state(translucentActivity, ON_RESUME)); |
| waitAndAssertActivityStates(state(secondTranslucentActivity, ON_DESTROY)); |
| LifecycleVerifier.assertResumeToDestroySequence(SecondTranslucentActivity.class, |
| getLifecycleLog()); |
| LifecycleVerifier.assertSequence(TranslucentActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_RESUME), "destroy"); |
| LifecycleVerifier.assertEmptySequence(FirstActivity.class, getLifecycleLog(), "destroy"); |
| |
| // Finish first translucent activity |
| getLifecycleLog().clear(); |
| translucentActivity.finish(); |
| |
| waitAndAssertActivityStates(state(firstActivity, ON_RESUME)); |
| waitAndAssertActivityStates(state(translucentActivity, ON_DESTROY)); |
| LifecycleVerifier.assertResumeToDestroySequence(TranslucentActivity.class, |
| getLifecycleLog()); |
| LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_RESUME), "secondDestroy"); |
| } |
| |
| @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(); |
| 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(); |
| 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(); |
| 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 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_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_ACTIVITY_RESULT, ON_RESUME, ON_TOP_POSITION_GAINED); |
| 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_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_STOP, ON_ACTIVITY_RESULT, ON_RESTART, ON_START, ON_RESUME, |
| ON_TOP_POSITION_GAINED); |
| final List<LifecycleLog.ActivityCallback> thirdSequence = |
| Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_STOP, ON_ACTIVITY_RESULT, ON_RESTART, ON_START, ON_RESUME, |
| ON_TOP_POSITION_GAINED); |
| final List<LifecycleLog.ActivityCallback> fourthSequence = |
| Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_STOP, ON_RESTART, ON_START, ON_ACTIVITY_RESULT, ON_RESUME, |
| ON_TOP_POSITION_GAINED); |
| LifecycleVerifier.assertSequenceMatchesOneOf(LaunchForResultActivity.class, |
| getLifecycleLog(), |
| Arrays.asList(expectedSequence, sequenceWithStop, thirdSequence, fourthSequence), |
| "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<List<LifecycleLog.ActivityCallback>> expectedSequences; |
| if (isTranslucent) { |
| expectedSequences = Arrays.asList( |
| Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, ON_PAUSE, |
| ON_ACTIVITY_RESULT, ON_RESUME, ON_TOP_POSITION_GAINED) |
| ); |
| } else { |
| expectedSequences = Arrays.asList( |
| Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_STOP, ON_RESTART, ON_START, ON_ACTIVITY_RESULT, ON_RESUME, |
| ON_TOP_POSITION_GAINED), |
| Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_STOP, ON_ACTIVITY_RESULT, ON_RESTART, ON_START, ON_RESUME, |
| ON_TOP_POSITION_GAINED) |
| ); |
| } |
| waitForActivityTransitions(LaunchForResultActivity.class, expectedSequences.get(0)); |
| |
| LifecycleVerifier.assertSequenceMatchesOneOf(LaunchForResultActivity.class, |
| getLifecycleLog(), expectedSequences, "activityResult"); |
| } |
| |
| @Test |
| public void testOnPostCreateAfterCreate() throws Exception { |
| final Activity callbackTrackingActivity = |
| mCallbackTrackingActivityTestRule.launchActivity(new Intent()); |
| |
| waitAndAssertActivityStates(state(callbackTrackingActivity, ON_TOP_POSITION_GAINED)); |
| |
| LifecycleVerifier.assertSequence(CallbackTrackingActivity.class, getLifecycleLog(), |
| Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED),"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_TOP_POSITION_GAINED)); |
| |
| // Call "recreate" and assert sequence |
| getLifecycleLog().clear(); |
| getInstrumentation().runOnMainSync(trackingActivity::recreate); |
| waitAndAssertActivityStates(state(trackingActivity, ON_TOP_POSITION_GAINED)); |
| |
| LifecycleVerifier.assertSequence(CallbackTrackingActivity.class, |
| getLifecycleLog(), |
| Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY, PRE_ON_CREATE, |
| ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, ON_TOP_POSITION_GAINED), |
| "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_TOP_POSITION_GAINED)); |
| |
| // 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(); |
| 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_TOP_POSITION_GAINED)); |
| |
| // 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(); |
| 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); |
| } |
| |
| /** |
| * Tests 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 |
| Activity secondActivity = mSecondActivityTestRule.launchActivity( |
| new Intent().setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)); |
| |
| // Wait for first activity to become stopped |
| final boolean secondActivityIsTranslucent = ActivityInfo.isTranslucentOrFloating( |
| secondActivity.getWindow().getWindowStyle()); |
| waitAndAssertActivityStates( |
| occludedActivityState(recreatingActivity, secondActivityIsTranslucent), |
| state(secondActivity, ON_RESUME)); |
| |
| // Launch the activity again to recreate |
| getLifecycleLog().clear(); |
| final Intent intent = new Intent(mContext, SingleTopActivity.class); |
| intent.putExtra(EXTRA_RECREATE, true); |
| intent.addFlags(FLAG_ACTIVITY_NEW_TASK); |
| mTargetContext.startActivity(intent); |
| |
| // Wait for activity to relaunch and resume |
| final List<List<LifecycleLog.ActivityCallback>> expectedRelaunchSequences; |
| if (secondActivityIsTranslucent) { |
| expectedRelaunchSequences = Arrays.asList(Arrays.asList(ON_NEW_INTENT, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_STOP, ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, |
| ON_POST_CREATE, ON_RESUME,ON_PAUSE, ON_RESUME, ON_TOP_POSITION_GAINED)); |
| } else { |
| expectedRelaunchSequences = Arrays.asList( |
| Arrays.asList(ON_RESTART, ON_START, ON_NEW_INTENT, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, |
| ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, |
| ON_RESUME, ON_TOP_POSITION_GAINED), |
| Arrays.asList(ON_NEW_INTENT, ON_RESTART, ON_START, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, |
| ON_DESTROY, PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, |
| ON_RESUME, ON_TOP_POSITION_GAINED)); |
| } |
| |
| waitForActivityTransitions(SingleTopActivity.class, expectedRelaunchSequences.get(0)); |
| LifecycleVerifier.assertSequenceMatchesOneOf(SingleTopActivity.class, getLifecycleLog(), |
| expectedRelaunchSequences, "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_TOP_POSITION_GAINED)); |
| LifecycleVerifier.assertLaunchSequence(SingleTopActivity.class, getLifecycleLog()); |
| |
| // Try to launch again |
| getLifecycleLog().clear(); |
| final Intent intent = new Intent(mContext, SingleTopActivity.class); |
| intent.addFlags(FLAG_ACTIVITY_NEW_TASK); |
| mTargetContext.startActivity(intent); |
| |
| // Wait for the activity to resume again |
| waitAndAssertActivityStates(state(singleTopActivity, ON_TOP_POSITION_GAINED)); |
| |
| // Verify that the first activity was paused, new intent was delivered and resumed again |
| LifecycleVerifier.assertSequence(SingleTopActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_NEW_INTENT, ON_RESUME, |
| ON_TOP_POSITION_GAINED), "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_TOP_POSITION_GAINED)); |
| LifecycleVerifier.assertLaunchSequence(SingleTopActivity.class, getLifecycleLog()); |
| |
| // Launch something on top |
| final Intent newTaskIntent = new Intent(); |
| newTaskIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | 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(mContext, SingleTopActivity.class); |
| intent.addFlags(FLAG_ACTIVITY_NEW_TASK); |
| mTargetContext.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_TOP_POSITION_GAINED)); |
| LifecycleVerifier.assertLaunchSequence(SingleTopActivity.class, getLifecycleLog()); |
| |
| // 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(mContext, SingleTopActivity.class); |
| intent.addFlags(FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); |
| mTargetContext.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 testFinishInOnCreate() throws Exception { |
| verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class, |
| EXTRA_FINISH_IN_ON_CREATE, "onCreate"); |
| } |
| |
| @Test |
| public void testFinishInOnCreateNoDisplay() throws Exception { |
| verifyFinishAtStage(mNoDisplayActivityTestRule, NoDisplayActivity.class, |
| EXTRA_FINISH_IN_ON_CREATE, "onCreate"); |
| } |
| |
| @Test |
| public void testFinishInOnStart() throws Exception { |
| verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class, |
| EXTRA_FINISH_IN_ON_START, "onStart"); |
| } |
| |
| @Test |
| public void testFinishInOnStartNoDisplay() throws Exception { |
| verifyFinishAtStage(mNoDisplayActivityTestRule, NoDisplayActivity.class, |
| EXTRA_FINISH_IN_ON_START, "onStart"); |
| } |
| |
| @Test |
| public void testFinishInOnResume() throws Exception { |
| verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class, |
| EXTRA_FINISH_IN_ON_RESUME, "onResume"); |
| } |
| |
| @Test |
| public void testFinishInOnResumeNoDisplay() throws Exception { |
| verifyFinishAtStage(mNoDisplayActivityTestRule, NoDisplayActivity.class, |
| EXTRA_FINISH_IN_ON_RESUME, "onResume"); |
| } |
| |
| private void verifyFinishAtStage(ActivityTestRule rule, Class<? extends Activity> activityClass, |
| String finishStageExtra, String stageName) { |
| final Intent intent = new Intent(); |
| intent.putExtra(finishStageExtra, true); |
| rule.launchActivity(intent); |
| |
| final List<LifecycleLog.ActivityCallback> expectedSequence = |
| LifecycleVerifier.getLaunchAndDestroySequence(activityClass); |
| waitAndAssertActivityTransitions(activityClass, expectedSequence, "finish in " + stageName); |
| } |
| |
| @Test |
| public void testFinishInOnPause() throws Exception { |
| verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class, |
| EXTRA_FINISH_IN_ON_PAUSE, "onPause", mTranslucentActivityTestRule); |
| } |
| |
| @Test |
| public void testFinishInOnStop() throws Exception { |
| verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class, |
| EXTRA_FINISH_IN_ON_STOP, "onStop", mFirstActivityTestRule); |
| } |
| |
| private void verifyFinishAtStage(ActivityTestRule rule, Class<? extends Activity> activityClass, |
| String finishStageExtra, String stageName, ActivityTestRule launchOnTopRule) { |
| final Intent intent = new Intent(); |
| intent.putExtra(finishStageExtra, true); |
| |
| // Activity will finish itself after onResume, so need to launch an extra activity on |
| // top to get it there. |
| final Activity testActivity = rule.launchActivity(intent); |
| |
| // Wait for the activity to resume and gain top position |
| waitAndAssertActivityStates(state(testActivity, ON_TOP_POSITION_GAINED)); |
| |
| // Launch an activity on top, which will make the first one paused or stopped. |
| launchOnTopRule.launchActivity(new Intent()); |
| |
| final List<LifecycleLog.ActivityCallback> expectedSequence = |
| LifecycleVerifier.getLaunchAndDestroySequence(activityClass); |
| waitAndAssertActivityTransitions(activityClass, expectedSequence, "finish in " + stageName); |
| |
| } |
| } |