blob: e96e01731df22f202d61a69cca4b4eb37ec8ceb8 [file] [log] [blame]
/*
* Copyright (C) 2019 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.wm.lifecycle;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_DESTROY;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_PAUSE;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_RESTART;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_RESUME;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_START;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_STOP;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_GAINED;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_LOST;
import static android.server.wm.lifecycle.LifecycleVerifier.transition;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import android.app.Activity;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import org.junit.Test;
import java.util.Arrays;
/**
* Tests for {@link Activity} class APIs.
*
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:ActivityTests
*/
@Presubmit
@MediumTest
@android.server.wm.annotation.Group3
public class ActivityTests extends ActivityLifecycleClientTestBase {
@Test
public void testReleaseActivityInstance_visible() throws Exception {
final Activity activity = launchActivityAndWait(FirstActivity.class);
waitAndAssertActivityStates(state(activity, ON_RESUME));
getLifecycleLog().clear();
assertFalse("Launched and visible activity must be released", activity.releaseInstance());
LifecycleVerifier.assertEmptySequence(FirstActivity.class, getLifecycleLog(),
"tryReleaseInstance");
}
@Test
public void testReleaseActivityInstance_invisible() throws Exception {
// Launch two activities - second one to cover the first one and make it invisible.
final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
final Activity secondActivity = launchActivityAndWait(SecondActivity.class);
waitAndAssertActivityStates(state(secondActivity, ON_RESUME),
state(firstActivity, ON_STOP));
// Wait for activity to report saved state to the server.
getInstrumentation().waitForIdleSync();
// Release the instance of the non-visible activity below.
getLifecycleLog().clear();
assertTrue("It must be possible to release an instance of an invisible activity",
firstActivity.releaseInstance());
waitAndAssertActivityStates(state(firstActivity, ON_DESTROY));
LifecycleVerifier.assertEmptySequence(SecondActivity.class, getLifecycleLog(),
"releaseInstance");
// Finish the top activity to navigate back to the first one and re-create it.
getLifecycleLog().clear();
secondActivity.finish();
waitAndAssertActivityStates(state(secondActivity, ON_DESTROY));
LifecycleVerifier.assertLaunchSequence(FirstActivity.class, getLifecycleLog());
}
/**
* Verify that {@link Activity#finishAndRemoveTask()} removes all activities in task if called
* for root of task.
*/
@FlakyTest(bugId=137329632)
@Test
public void testFinishTask_FromRoot() throws Exception {
final Class<? extends Activity> rootActivityClass = CallbackTrackingActivity.class;
final Activity rootActivity = launchActivityAndWait(rootActivityClass);
final Class<? extends Activity> topActivityClass = SecondCallbackTrackingActivity.class;
final Activity topActivity = launchActivityAndWait(topActivityClass);
waitAndAssertActivityStates(state(rootActivity, ON_STOP),
state(topActivity, ON_TOP_POSITION_GAINED));
getLifecycleLog().clear();
rootActivity.finishAndRemoveTask();
waitAndAssertActivityStates(state(rootActivity, ON_DESTROY),
state(topActivity, ON_DESTROY));
// Cannot guarantee exact sequence among top and bottom activities, so verifying
// independently
LifecycleVerifier.assertSequence(rootActivityClass, getLifecycleLog(),
Arrays.asList(ON_DESTROY), "finishAndRemoveTask");
LifecycleVerifier.assertSequence(topActivityClass, getLifecycleLog(),
Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY),
"finishAndRemoveTask");
}
/**
* Verify that {@link Activity#finishAndRemoveTask()} removes all activities in task if called
* for root of task. This version verifies lifecycle when top activity is translucent
*/
@FlakyTest(bugId=137329632)
@Test
public void testFinishTask_FromRoot_TranslucentOnTop() throws Exception {
final Class<? extends Activity> rootActivityClass = CallbackTrackingActivity.class;
final Activity rootActivity = launchActivityAndWait(rootActivityClass);
final Class<? extends Activity> topActivityClass =
TranslucentCallbackTrackingActivity.class;
final Activity topActivity = launchActivityAndWait(topActivityClass);
waitAndAssertActivityStates(state(rootActivity, ON_PAUSE),
state(topActivity, ON_TOP_POSITION_GAINED));
getLifecycleLog().clear();
rootActivity.finishAndRemoveTask();
waitAndAssertActivityStates(state(rootActivity, ON_DESTROY),
state(topActivity, ON_DESTROY));
// Cannot guarantee exact sequence among top and bottom activities, so verifying
// independently
LifecycleVerifier.assertSequence(rootActivityClass, getLifecycleLog(),
Arrays.asList(ON_STOP, ON_DESTROY), "finishAndRemoveTask");
LifecycleVerifier.assertSequence(topActivityClass, getLifecycleLog(),
Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY),
"finishAndRemoveTask");
}
/**
* Verify that {@link Activity#finishAndRemoveTask()} only removes one activity in task if
* called not for root of task.
*/
@FlakyTest(bugId=137329632)
@Test
public void testFinishTask_NotFromRoot() throws Exception {
final Class<? extends Activity> rootActivityClass = CallbackTrackingActivity.class;
final Activity rootActivity = launchActivityAndWait(rootActivityClass);
final Class<? extends Activity> midActivityClass = SecondActivity.class;
final Activity midActivity = launchActivityAndWait(midActivityClass);
final Class<? extends Activity> topActivityClass = SecondCallbackTrackingActivity.class;
final Activity topActivity = launchActivityAndWait(topActivityClass);
waitAndAssertActivityStates(state(rootActivity, ON_STOP), state(midActivity, ON_STOP),
state(topActivity, ON_TOP_POSITION_GAINED));
getLifecycleLog().clear();
midActivity.finishAndRemoveTask();
waitAndAssertActivityStates(state(midActivity, ON_DESTROY));
LifecycleVerifier.assertEntireSequence(Arrays.asList(
transition(midActivityClass, ON_DESTROY)), getLifecycleLog(),
"finishAndRemoveTask");
}
/**
* Verify the lifecycle of {@link Activity#finishAfterTransition()} for activity that has a
* transition set.
*/
@FlakyTest(bugId=137329632)
@Test
public void testFinishAfterTransition() throws Exception {
final TransitionSourceActivity rootActivity =
(TransitionSourceActivity) launchActivityAndWait(TransitionSourceActivity.class);
waitAndAssertActivityStates(state(rootActivity, ON_RESUME));
// Launch activity with configured shared element transition. It will call
// finishAfterTransition() on its own after transition completes.
rootActivity.runOnUiThread(() -> rootActivity.launchActivityWithTransition());
waitAndAssertActivityStates(state(TransitionDestinationActivity.class, ON_DESTROY),
state(rootActivity, ON_RESUME));
LifecycleVerifier.assertLaunchAndDestroySequence(TransitionDestinationActivity.class,
getLifecycleLog());
}
/**
* Verify the lifecycle of {@link Activity#finishAfterTransition()} for activity with no
* transition set (root of task).
*/
@Test
public void testFinishAfterTransition_noTransition_rootOfTask() throws Exception {
final Activity activity = launchActivityAndWait(FirstActivity.class);
waitAndAssertActivityStates(state(activity, ON_RESUME));
getLifecycleLog().clear();
activity.finishAfterTransition();
waitAndAssertActivityStates(state(FirstActivity.class, ON_DESTROY));
LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
Arrays.asList(ON_PAUSE, ON_STOP, ON_DESTROY), "finishAfterTransition");
}
/**
* Verify the lifecycle of {@link Activity#finishAfterTransition()} for activity with no
* transition set.
*/
@FlakyTest(bugId=137329632)
@Test
public void testFinishAfterTransition_noTransition() throws Exception {
final Activity rootActivity = launchActivityAndWait(FirstActivity.class);
final Activity topActivity = launchActivityAndWait(SecondActivity.class);
waitAndAssertActivityStates(state(topActivity, ON_RESUME), state(rootActivity, ON_STOP));
getLifecycleLog().clear();
topActivity.finishAfterTransition();
waitAndAssertActivityStates(state(SecondActivity.class, ON_DESTROY));
LifecycleVerifier.assertSequence(SecondActivity.class, getLifecycleLog(),
Arrays.asList(ON_PAUSE, ON_STOP, ON_DESTROY), "finishAfterTransition");
}
/**
* Verify that {@link Activity#finishAffinity()} will finish all activities with the same
* affinity below the target activity.
*/
@Test
public void testFinishAffinity() throws Exception {
final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
final Activity secondActivity = launchActivityAndWait(SecondActivity.class);
final Activity thirdActivity = launchActivityAndWait(ThirdActivity.class);
waitAndAssertActivityStates(state(thirdActivity, ON_RESUME), state(secondActivity, ON_STOP),
state(firstActivity, ON_STOP));
getLifecycleLog().clear();
secondActivity.finishAffinity();
waitAndAssertActivityStates(state(FirstActivity.class, ON_DESTROY),
state(SecondActivity.class, ON_DESTROY));
LifecycleVerifier.assertEmptySequence(ThirdActivity.class, getLifecycleLog(),
"finishAffinityBelow");
}
/**
* Verify that {@link Activity#finishAffinity()} will not finish activities with different
* affinities in the same task.
*/
@Test
public void testFinishAffinity_differentAffinity() throws Exception {
final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
final Activity differentAffinityActivity = new Launcher(DifferentAffinityActivity.class)
.setOptions(getLaunchOptionsForFullscreen())
.launch();
waitAndAssertActivityStates(state(differentAffinityActivity, ON_RESUME),
state(firstActivity, ON_STOP));
getLifecycleLog().clear();
differentAffinityActivity.finishAffinity();
waitAndAssertActivityStates(state(DifferentAffinityActivity.class, ON_DESTROY));
LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
Arrays.asList(ON_RESTART, ON_START, ON_RESUME), "finishAffinity");
}
/**
* Verify that {@link Activity#finishAffinity()} will not finish activities with the same
* affinity in different tasks.
*/
@Test
public void testFinishAffinity_multiTask() throws Exception {
final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
// Launch fullscreen activity in a new task to stop first activity
final Activity secondActivity = new Launcher(SecondActivity.class)
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
.setOptions(getLaunchOptionsForFullscreen())
.launch();
waitAndAssertActivityStates(state(secondActivity, ON_RESUME),
state(firstActivity, ON_STOP));
getLifecycleLog().clear();
secondActivity.finishAffinity();
waitAndAssertActivityStates(state(SecondActivity.class, ON_DESTROY),
state(firstActivity, ON_RESUME));
}
}