blob: f6f8ff096cab87217ef2dcc7fa876fcf152b59bd [file] [log] [blame]
/*
* 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);
}
}