1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package android.server.wm.lifecycle; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 20 import static android.content.Intent.FLAG_ACTIVITY_FORWARD_RESULT; 21 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; 22 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 23 import static android.server.wm.StateLogger.log; 24 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP; 25 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_ACTIVITY_RESULT; 26 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_CREATE; 27 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_DESTROY; 28 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED; 29 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_NEW_INTENT; 30 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_PAUSE; 31 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_POST_CREATE; 32 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_RESTART; 33 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_RESUME; 34 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_START; 35 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_STOP; 36 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_GAINED; 37 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_LOST; 38 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_USER_LEAVE_HINT; 39 40 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 41 42 import static org.hamcrest.Matchers.lessThan; 43 import static org.junit.Assert.fail; 44 45 import android.app.Activity; 46 import android.app.ActivityOptions; 47 import android.app.PictureInPictureParams; 48 import android.content.ComponentName; 49 import android.content.Context; 50 import android.content.Intent; 51 import android.content.pm.ActivityInfo; 52 import android.content.res.Configuration; 53 import android.os.Bundle; 54 import android.os.Looper; 55 import android.server.wm.MultiDisplayTestBase; 56 import android.server.wm.ObjectTracker; 57 import android.server.wm.cts.R; 58 import android.server.wm.lifecycle.LifecycleLog.ActivityCallback; 59 import android.transition.Transition; 60 import android.transition.TransitionListenerAdapter; 61 import android.util.Pair; 62 63 import androidx.annotation.NonNull; 64 import androidx.test.rule.ActivityTestRule; 65 66 import com.android.compatibility.common.util.SystemUtil; 67 68 import org.junit.Assert; 69 import org.junit.Before; 70 71 import java.util.ArrayList; 72 import java.util.Arrays; 73 import java.util.Collections; 74 import java.util.List; 75 import java.util.function.Consumer; 76 77 /** Base class for device-side tests that verify correct activity lifecycle transitions. */ 78 public class ActivityLifecycleClientTestBase extends MultiDisplayTestBase { 79 80 /** 81 * Activity launch time is evaluated. It is expected to be less than 5 seconds. Otherwise, it's 82 * likely there is a timeout. 83 */ 84 private static final long ACTIVITY_LAUNCH_TIMEOUT = 5 * 1000; 85 86 static final String EXTRA_RECREATE = "recreate"; 87 static final String EXTRA_FINISH_IN_ON_CREATE = "finish_in_on_create"; 88 static final String EXTRA_FINISH_IN_ON_START = "finish_in_on_start"; 89 static final String EXTRA_FINISH_IN_ON_RESUME = "finish_in_on_resume"; 90 static final String EXTRA_FINISH_IN_ON_PAUSE = "finish_in_on_pause"; 91 static final String EXTRA_FINISH_IN_ON_STOP = "finish_in_on_stop"; 92 static final String EXTRA_START_ACTIVITY_IN_ON_CREATE = "start_activity_in_on_create"; 93 static final String EXTRA_START_ACTIVITY_WHEN_IDLE = "start_activity_when_idle"; 94 static final String EXTRA_ACTIVITY_ON_USER_LEAVE_HINT = "activity_on_user_leave_hint"; 95 /** 96 * Use this flag to skip recording top resumed state to avoid affecting verification. 97 * @see Launcher#setSkipTopResumedStateCheck() 98 */ 99 static final String EXTRA_SKIP_TOP_RESUMED_STATE = "skip_top_resumed_state"; 100 101 static final ComponentName CALLBACK_TRACKING_ACTIVITY = 102 getComponentName(CallbackTrackingActivity.class); 103 104 static final ComponentName CONFIG_CHANGE_HANDLING_ACTIVITY = 105 getComponentName(ConfigChangeHandlingActivity.class); 106 107 final ActivityTestRule mSlowActivityTestRule = new ActivityTestRule<>( 108 SlowActivity.class, true /* initialTouchMode */, false /* launchActivity */); 109 110 private static LifecycleLog mLifecycleLog; 111 112 protected Context mTargetContext; 113 private LifecycleTracker mLifecycleTracker; 114 115 @Before 116 @Override setUp()117 public void setUp() throws Exception { 118 super.setUp(); 119 120 mTargetContext = getInstrumentation().getTargetContext(); 121 // Log transitions for all activities that belong to this app. 122 mLifecycleLog = new LifecycleLog(); 123 mLifecycleLog.clear(); 124 125 // Track transitions and allow waiting for pending activity states. 126 mLifecycleTracker = new LifecycleTracker(mLifecycleLog); 127 128 // Some lifecycle tracking activities that have not been destroyed may affect the 129 // verification of next test because of the lifecycle log. We need to wait them to be 130 // destroyed in tearDown. 131 mShouldWaitForAllNonHomeActivitiesToDestroyed = true; 132 } 133 134 /** Activity launch builder for lifecycle tests. */ 135 class Launcher implements ObjectTracker.Consumable { 136 private int mFlags; 137 private ActivityCallback mExpectedState; 138 private List<String> mExtraFlags = new ArrayList<>(); 139 private Consumer<Intent> mPostIntentSetup; 140 private ActivityOptions mOptions; 141 private boolean mNoInstance; 142 private final Class<? extends Activity> mActivityClass; 143 private boolean mSkipLaunchTimeCheck; 144 private boolean mSkipTopResumedStateCheck; 145 146 private boolean mLaunchCalled = false; 147 148 /** 149 * @param activityClass Class of the activity to launch. 150 */ Launcher(@onNull Class<? extends Activity> activityClass)151 Launcher(@NonNull Class<? extends Activity> activityClass) { 152 mActivityClass = activityClass; 153 mObjectTracker.track(this); 154 } 155 156 /** 157 * Perform the activity launch. Will wait for an instance of the activity if needed and will 158 * verify the launch time. 159 */ launch()160 Activity launch() throws Exception { 161 mLaunchCalled = true; 162 163 // Prepare the intent 164 final Intent intent = new Intent(mTargetContext, mActivityClass); 165 if (mFlags != 0) { 166 intent.setFlags(mFlags); 167 } else { 168 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 169 } 170 for (String flag : mExtraFlags) { 171 intent.putExtra(flag, true); 172 } 173 if (mSkipTopResumedStateCheck) { 174 intent.putExtra(EXTRA_SKIP_TOP_RESUMED_STATE, true); 175 } 176 if (mPostIntentSetup != null) { 177 mPostIntentSetup.accept(intent); 178 } 179 final Bundle optionsBundle = mOptions != null ? mOptions.toBundle() : null; 180 181 // Start measuring time spent on starting the activity 182 final long startTime = System.currentTimeMillis(); 183 final Activity activity = SystemUtil.callWithShellPermissionIdentity(() -> { 184 if (mNoInstance) { 185 mTargetContext.startActivity(intent, optionsBundle); 186 return null; 187 } 188 return getInstrumentation().startActivitySync( 189 intent, optionsBundle); 190 }); 191 if (!mNoInstance && activity == null) { 192 fail("Must have returned an instance of Activity after launch."); 193 } 194 // Wait for activity to reach the desired state and verify launch time. 195 if (mExpectedState == null) { 196 mExpectedState = mSkipTopResumedStateCheck 197 || !CallbackTrackingActivity.class.isAssignableFrom(mActivityClass) 198 ? ON_RESUME : ON_TOP_POSITION_GAINED; 199 } 200 waitAndAssertActivityStates(state(mActivityClass, mExpectedState)); 201 if (!mSkipLaunchTimeCheck) { 202 Assert.assertThat(System.currentTimeMillis() - startTime, 203 lessThan(ACTIVITY_LAUNCH_TIMEOUT)); 204 } 205 206 return activity; 207 } 208 209 /** Set intent flags for launch. */ setFlags(int flags)210 public Launcher setFlags(int flags) { 211 mFlags = flags; 212 return this; 213 } 214 215 /** 216 * Set the expected lifecycle state to verify. Will be inferred automatically if not set. 217 */ setExpectedState(ActivityCallback expectedState)218 public Launcher setExpectedState(ActivityCallback expectedState) { 219 mExpectedState = expectedState; 220 return this; 221 } 222 223 /** Allow the caller to customize the intent right before starting activity. */ customizeIntent(Consumer<Intent> intentSetup)224 public Launcher customizeIntent(Consumer<Intent> intentSetup) { 225 mPostIntentSetup = intentSetup; 226 return this; 227 } 228 229 /** Set extra flags to pass as boolean values through the intent. */ setExtraFlags(String... extraFlags)230 public Launcher setExtraFlags(String... extraFlags) { 231 mExtraFlags.addAll(Arrays.asList(extraFlags)); 232 return this; 233 } 234 235 /** Set the activity options to use for the launch. */ setOptions(ActivityOptions options)236 public Launcher setOptions(ActivityOptions options) { 237 mOptions = options; 238 return this; 239 } 240 241 /** 242 * Indicate that no instance should be returned. Usually used for activity launches that are 243 * expected to end up in not-active state and when the synchronous instrumentation launch 244 * can timeout. 245 */ setNoInstance()246 Launcher setNoInstance() { 247 mNoInstance = true; 248 return this; 249 } 250 251 /** Indicate that launch time verification should not be performed. */ setSkipLaunchTimeCheck()252 Launcher setSkipLaunchTimeCheck() { 253 mSkipLaunchTimeCheck = true; 254 return this; 255 } 256 257 /** 258 * There is no guarantee that an activity will get top resumed state, especially if it 259 * finishes itself in onResumed(), like a trampoline activity. Set to skip recording 260 * top resumed state to avoid affecting verification. 261 */ setSkipTopResumedStateCheck()262 Launcher setSkipTopResumedStateCheck() { 263 mSkipTopResumedStateCheck = true; 264 return this; 265 } 266 267 @Override isConsumed()268 public boolean isConsumed() { 269 return mLaunchCalled; 270 } 271 } 272 273 /** 274 * Launch an activity given a class. Will wait for the launch to finish and verify the launch 275 * time. 276 * @return The launched Activity instance. 277 */ 278 @SuppressWarnings("unchecked") launchActivityAndWait(Class<? extends Activity> activityClass)279 <T extends Activity> T launchActivityAndWait(Class<? extends Activity> activityClass) 280 throws Exception { 281 return (T) new Launcher(activityClass).launch(); 282 } 283 284 /** 285 * Blocking call that will wait for activities to reach expected states with timeout. 286 */ 287 @SafeVarargs waitAndAssertActivityStates( Pair<Class<? extends Activity>, ActivityCallback>.... activityCallbacks)288 final void waitAndAssertActivityStates( 289 Pair<Class<? extends Activity>, ActivityCallback>... activityCallbacks) { 290 log("Start waitAndAssertActivityCallbacks"); 291 mLifecycleTracker.waitAndAssertActivityStates(activityCallbacks); 292 } 293 294 /** 295 * Blocking call that will wait and verify that the activity transition settles with the 296 * expected state. 297 */ waitAndAssertActivityCurrentState( Class<? extends Activity> activityClass, ActivityCallback expectedState)298 final void waitAndAssertActivityCurrentState( 299 Class<? extends Activity> activityClass, ActivityCallback expectedState) { 300 log("Start waitAndAssertActivityCurrentState"); 301 mLifecycleTracker.waitAndAssertActivityCurrentState(activityClass, expectedState); 302 } 303 304 /** 305 * Blocking call that will wait for activities to perform the expected sequence of transitions. 306 * @see LifecycleTracker#waitForActivityTransitions(Class, List) 307 */ waitForActivityTransitions(Class<? extends Activity> activityClass, List<ActivityCallback> expectedTransitions)308 final void waitForActivityTransitions(Class<? extends Activity> activityClass, 309 List<ActivityCallback> expectedTransitions) { 310 log("Start waitForActivityTransitions"); 311 mLifecycleTracker.waitForActivityTransitions(activityClass, expectedTransitions); 312 } 313 314 /** 315 * Blocking call that will wait for activities to perform the expected sequence of transitions. 316 * After waiting it asserts that the sequence matches the expected. 317 * @see LifecycleTracker#waitForActivityTransitions(Class, List) 318 */ waitAndAssertActivityTransitions(Class<? extends Activity> activityClass, List<ActivityCallback> expectedTransitions, String message)319 final void waitAndAssertActivityTransitions(Class<? extends Activity> activityClass, 320 List<ActivityCallback> expectedTransitions, String message) { 321 log("Start waitAndAssertActivityTransition"); 322 mLifecycleTracker.waitForActivityTransitions(activityClass, expectedTransitions); 323 324 LifecycleVerifier.assertSequence(activityClass, getLifecycleLog(), expectedTransitions, 325 message); 326 } 327 getLifecycleLog()328 LifecycleLog getLifecycleLog() { 329 return mLifecycleLog; 330 } 331 state(Activity activity, ActivityCallback stage)332 static Pair<Class<? extends Activity>, ActivityCallback> state(Activity activity, 333 ActivityCallback stage) { 334 return state(activity.getClass(), stage); 335 } 336 state( Class<? extends Activity> activityClass, ActivityCallback stage)337 static Pair<Class<? extends Activity>, ActivityCallback> state( 338 Class<? extends Activity> activityClass, ActivityCallback stage) { 339 return new Pair<>(activityClass, stage); 340 } 341 342 /** 343 * Returns a pair of the activity and the state it should be in based on the configuration of 344 * occludingActivity. 345 */ occludedActivityState( Activity activity, Activity occludingActivity)346 static Pair<Class<? extends Activity>, ActivityCallback> occludedActivityState( 347 Activity activity, Activity occludingActivity) { 348 return occludedActivityState(activity, isTranslucent(occludingActivity)); 349 } 350 351 /** 352 * Returns a pair of the activity and the state it should be in based on 353 * occludingActivityIsTranslucent. 354 */ occludedActivityState( Activity activity, boolean occludingActivityIsTranslucent)355 static Pair<Class<? extends Activity>, ActivityCallback> occludedActivityState( 356 Activity activity, boolean occludingActivityIsTranslucent) { 357 // Activities behind a translucent activity should be in the paused state since they are 358 // still visible. Otherwise, they should be in the stopped state. 359 return state(activity, occludedActivityState(occludingActivityIsTranslucent)); 360 } 361 occludedActivityState(boolean occludingActivityIsTranslucent)362 static ActivityCallback occludedActivityState(boolean occludingActivityIsTranslucent) { 363 return occludingActivityIsTranslucent ? ON_PAUSE : ON_STOP; 364 } 365 366 /** Returns true if the input activity is translucent. */ isTranslucent(Activity activity)367 static boolean isTranslucent(Activity activity) { 368 return ActivityInfo.isTranslucentOrFloating(activity.getWindow().getWindowStyle()); 369 } 370 371 /** Base activity that only tracks fundamental activity lifecycle states. */ 372 public static class LifecycleTrackingActivity extends Activity { 373 LifecycleLog.LifecycleLogClient mLifecycleLogClient; 374 375 @Override onCreate(Bundle savedInstanceState)376 protected void onCreate(Bundle savedInstanceState) { 377 super.onCreate(savedInstanceState); 378 mLifecycleLogClient = LifecycleLog.LifecycleLogClient.create(this); 379 mLifecycleLogClient.onActivityCallback(ON_CREATE); 380 381 final Intent intent = getIntent(); 382 final Intent startOnCreate = 383 intent.getParcelableExtra(EXTRA_START_ACTIVITY_IN_ON_CREATE); 384 if (startOnCreate != null) { 385 startActivity(startOnCreate); 386 } 387 388 final Intent startOnIdle = intent.getParcelableExtra(EXTRA_START_ACTIVITY_WHEN_IDLE); 389 if (startOnIdle != null) { 390 Looper.getMainLooper().getQueue().addIdleHandler(() -> { 391 startActivity(startOnIdle); 392 return false; 393 }); 394 } 395 396 if (intent.getBooleanExtra(EXTRA_FINISH_IN_ON_CREATE, false)) { 397 finish(); 398 } 399 } 400 401 @Override onStart()402 protected void onStart() { 403 super.onStart(); 404 mLifecycleLogClient.onActivityCallback(ON_START); 405 406 if (getIntent().getBooleanExtra(EXTRA_FINISH_IN_ON_START, false)) { 407 finish(); 408 } 409 } 410 411 @Override onResume()412 protected void onResume() { 413 super.onResume(); 414 mLifecycleLogClient.onActivityCallback(ON_RESUME); 415 416 final Intent intent = getIntent(); 417 if (intent.getBooleanExtra(EXTRA_FINISH_IN_ON_RESUME, false)) { 418 finish(); 419 } 420 } 421 422 @Override onPause()423 protected void onPause() { 424 super.onPause(); 425 mLifecycleLogClient.onActivityCallback(ON_PAUSE); 426 427 if (getIntent().getBooleanExtra(EXTRA_FINISH_IN_ON_PAUSE, false)) { 428 finish(); 429 } 430 } 431 432 @Override onStop()433 protected void onStop() { 434 super.onStop(); 435 mLifecycleLogClient.onActivityCallback(ON_STOP); 436 437 if (getIntent().getBooleanExtra(EXTRA_FINISH_IN_ON_STOP, false)) { 438 finish(); 439 } 440 } 441 442 @Override onDestroy()443 protected void onDestroy() { 444 super.onDestroy(); 445 mLifecycleLogClient.onActivityCallback(ON_DESTROY); 446 mLifecycleLogClient.close(); 447 } 448 449 @Override onRestart()450 protected void onRestart() { 451 super.onRestart(); 452 mLifecycleLogClient.onActivityCallback(ON_RESTART); 453 } 454 455 @Override onUserLeaveHint()456 protected void onUserLeaveHint() { 457 super.onUserLeaveHint(); 458 459 if (getIntent().getBooleanExtra(EXTRA_ACTIVITY_ON_USER_LEAVE_HINT, false)) { 460 mLifecycleLogClient.onActivityCallback(ON_USER_LEAVE_HINT); 461 } 462 } 463 } 464 465 // Test activity 466 public static class FirstActivity extends LifecycleTrackingActivity { 467 } 468 469 // Test activity 470 public static class SecondActivity extends LifecycleTrackingActivity { 471 } 472 473 // Test activity 474 public static class ThirdActivity extends LifecycleTrackingActivity { 475 } 476 477 // Test activity 478 public static class SideActivity extends LifecycleTrackingActivity { 479 } 480 481 // Translucent test activity 482 public static class TranslucentActivity extends LifecycleTrackingActivity { 483 } 484 485 // Translucent test activity 486 public static class SecondTranslucentActivity extends LifecycleTrackingActivity { 487 } 488 489 /** 490 * Base activity that records callbacks in addition to main lifecycle transitions. 491 */ 492 public static class CallbackTrackingActivity extends LifecycleTrackingActivity { 493 494 @Override onActivityResult(int requestCode, int resultCode, Intent data)495 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 496 super.onActivityResult(requestCode, resultCode, data); 497 mLifecycleLogClient.onActivityCallback(ON_ACTIVITY_RESULT); 498 } 499 500 @Override onPostCreate(Bundle savedInstanceState)501 protected void onPostCreate(Bundle savedInstanceState) { 502 super.onPostCreate(savedInstanceState); 503 mLifecycleLogClient.onActivityCallback(ON_POST_CREATE); 504 } 505 506 @Override onNewIntent(Intent intent)507 protected void onNewIntent(Intent intent) { 508 super.onNewIntent(intent); 509 mLifecycleLogClient.onActivityCallback(ON_NEW_INTENT); 510 setIntent(intent); 511 } 512 513 @Override onTopResumedActivityChanged(boolean isTopResumedActivity)514 public void onTopResumedActivityChanged(boolean isTopResumedActivity) { 515 if (!getIntent().getBooleanExtra(EXTRA_SKIP_TOP_RESUMED_STATE, false)) { 516 mLifecycleLogClient.onActivityCallback( 517 isTopResumedActivity ? ON_TOP_POSITION_GAINED : ON_TOP_POSITION_LOST); 518 } 519 } 520 521 @Override onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig)522 public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) { 523 mLifecycleLogClient.onActivityCallback(ON_MULTI_WINDOW_MODE_CHANGED); 524 } 525 } 526 527 // Just another callback tracking activity, nothing special. 528 public static class SecondCallbackTrackingActivity extends CallbackTrackingActivity { 529 } 530 531 // Translucent callback tracking test activity 532 public static class TranslucentCallbackTrackingActivity extends CallbackTrackingActivity { 533 } 534 535 // Callback tracking activity that supports being shown on top of lock screen 536 public static class ShowWhenLockedCallbackTrackingActivity extends CallbackTrackingActivity { 537 @Override onCreate(Bundle savedInstanceState)538 protected void onCreate(Bundle savedInstanceState) { 539 super.onCreate(savedInstanceState); 540 setShowWhenLocked(true); 541 } 542 } 543 544 /** 545 * Test activity that launches {@link TrampolineActivity} for result. 546 */ 547 public static class LaunchForwardResultActivity extends CallbackTrackingActivity { 548 @Override onCreate(Bundle savedInstanceState)549 protected void onCreate(Bundle savedInstanceState) { 550 super.onCreate(savedInstanceState); 551 final Intent intent = new Intent(this, TrampolineActivity.class); 552 startActivityForResult(intent, 1 /* requestCode */); 553 } 554 } 555 556 public static class TrampolineActivity extends CallbackTrackingActivity { 557 @Override onCreate(Bundle savedInstanceState)558 protected void onCreate(Bundle savedInstanceState) { 559 super.onCreate(savedInstanceState); 560 final Intent intent = new Intent(this, ResultActivity.class); 561 intent.setFlags(FLAG_ACTIVITY_FORWARD_RESULT); 562 startActivity(intent); 563 finish(); 564 } 565 } 566 567 /** 568 * Test activity that launches {@link ResultActivity} for result. 569 */ 570 public static class LaunchForResultActivity extends CallbackTrackingActivity { 571 private static final String EXTRA_FORWARD_EXTRAS = "FORWARD_EXTRAS"; 572 public static final String EXTRA_LAUNCH_ON_RESULT = "LAUNCH_ON_RESULT"; 573 public static final String EXTRA_LAUNCH_ON_RESUME_AFTER_RESULT = 574 "LAUNCH_ON_RESUME_AFTER_RESULT"; 575 public static final String EXTRA_USE_TRANSLUCENT_RESULT = 576 "USE_TRANSLUCENT_RESULT"; 577 578 boolean mReceivedResultOk; 579 580 /** Adds the flag to the extra of intent which will forward to {@link ResultActivity}. */ forwardFlag(String... flags)581 static Consumer<Intent> forwardFlag(String... flags) { 582 return intent -> { 583 final Bundle data = new Bundle(); 584 for (String f : flags) { 585 data.putBoolean(f, true); 586 } 587 intent.putExtra(EXTRA_FORWARD_EXTRAS, data); 588 }; 589 } 590 591 @Override onCreate(Bundle savedInstanceState)592 protected void onCreate(Bundle savedInstanceState) { 593 super.onCreate(savedInstanceState); 594 595 final Intent intent; 596 if (getIntent().hasExtra(EXTRA_USE_TRANSLUCENT_RESULT)) { 597 intent = new Intent(this, TranslucentResultActivity.class); 598 } else { 599 intent = new Intent(this, ResultActivity.class); 600 } 601 602 final Bundle forwardExtras = getIntent().getBundleExtra(EXTRA_FORWARD_EXTRAS); 603 if (forwardExtras != null) { 604 intent.putExtras(forwardExtras); 605 } 606 startActivityForResult(intent, 1 /* requestCode */); 607 } 608 609 @Override onResume()610 protected void onResume() { 611 super.onResume(); 612 if (mReceivedResultOk 613 && getIntent().getBooleanExtra(EXTRA_LAUNCH_ON_RESUME_AFTER_RESULT, false)) { 614 startActivity(new Intent(this, CallbackTrackingActivity.class)); 615 } 616 } 617 618 @Override onActivityResult(int requestCode, int resultCode, Intent data)619 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 620 super.onActivityResult(requestCode, resultCode, data); 621 mReceivedResultOk = resultCode == RESULT_OK; 622 if (mReceivedResultOk && getIntent().getBooleanExtra(EXTRA_LAUNCH_ON_RESULT, false)) { 623 startActivity(new Intent(this, CallbackTrackingActivity.class)); 624 } 625 } 626 } 627 628 /** Translucent activity that is started for result. */ 629 public static class TranslucentResultActivity extends ResultActivity { 630 } 631 632 /** Test activity that is started for result. */ 633 public static class ResultActivity extends CallbackTrackingActivity { 634 @Override onCreate(Bundle savedInstanceState)635 protected void onCreate(Bundle savedInstanceState) { 636 setResult(RESULT_OK); 637 super.onCreate(savedInstanceState); 638 } 639 } 640 641 /** Test activity with NoDisplay theme that can finish itself. */ 642 public static class NoDisplayActivity extends ResultActivity { 643 static final String EXTRA_LAUNCH_ACTIVITY = "extra_launch_activity"; 644 static final String EXTRA_NEW_TASK = "extra_new_task"; 645 646 @Override onCreate(Bundle savedInstanceState)647 protected void onCreate(Bundle savedInstanceState) { 648 super.onCreate(savedInstanceState); 649 650 if (getIntent().getBooleanExtra(EXTRA_LAUNCH_ACTIVITY, false)) { 651 final Intent intent = new Intent(this, CallbackTrackingActivity.class); 652 if (getIntent().getBooleanExtra(EXTRA_NEW_TASK, false)) { 653 intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK); 654 } 655 startActivity(intent); 656 } 657 } 658 } 659 660 /** Test activity that can call {@link Activity#recreate()} if requested in a new intent. */ 661 public static class SingleTopActivity extends CallbackTrackingActivity { 662 static final String EXTRA_LAUNCH_ACTIVITY = "extra_launch_activity"; 663 static final String EXTRA_NEW_TASK = "extra_new_task"; 664 @Override onNewIntent(Intent intent)665 protected void onNewIntent(Intent intent) { 666 super.onNewIntent(intent); 667 if (intent != null && intent.getBooleanExtra(EXTRA_RECREATE, false)) { 668 recreate(); 669 } 670 } 671 672 @Override onCreate(Bundle savedInstanceState)673 protected void onCreate(Bundle savedInstanceState) { 674 super.onCreate(savedInstanceState); 675 676 if (getIntent().getBooleanExtra(EXTRA_LAUNCH_ACTIVITY, false)) { 677 final Intent intent = new Intent(this, SingleTopActivity.class); 678 if (getIntent().getBooleanExtra(EXTRA_NEW_TASK, false)) { 679 intent.setFlags(FLAG_ACTIVITY_NEW_TASK); 680 } 681 startActivityForResult(intent, 1 /* requestCode */); 682 } 683 } 684 } 685 686 // Config change handling activity 687 public static class ConfigChangeHandlingActivity extends CallbackTrackingActivity { 688 } 689 690 // Callback tracking activity that runs in a separate process 691 public static class SecondProcessCallbackTrackingActivity extends CallbackTrackingActivity { 692 } 693 694 // Pip-capable activity 695 // TODO(b/123013403): Disabled onMultiWindowMode changed callbacks to make the tests pass, so 696 // that they can verify other lifecycle transitions. This should be fixed and switched to 697 // extend CallbackTrackingActivity. 698 public static class PipActivity extends LifecycleTrackingActivity { 699 @Override onCreate(Bundle savedInstanceState)700 protected void onCreate(Bundle savedInstanceState) { 701 super.onCreate(savedInstanceState); 702 703 // Enter picture in picture with the given aspect ratio if provided 704 if (getIntent().hasExtra(EXTRA_ENTER_PIP)) { 705 enterPip(); 706 } 707 } 708 enterPip()709 void enterPip() { 710 enterPictureInPictureMode(new PictureInPictureParams.Builder().build()); 711 } 712 } 713 714 public static class AlwaysFocusablePipActivity extends CallbackTrackingActivity { 715 } 716 717 public static class SlowActivity extends CallbackTrackingActivity { 718 719 static final String EXTRA_CONTROL_FLAGS = "extra_control_flags"; 720 static final int FLAG_SLOW_TOP_RESUME_RELEASE = 0x00000001; 721 static final int FLAG_TIMEOUT_TOP_RESUME_RELEASE = 0x00000002; 722 723 private int mFlags; 724 725 @Override onCreate(Bundle savedInstanceState)726 protected void onCreate(Bundle savedInstanceState) { 727 super.onCreate(savedInstanceState); 728 mFlags = getIntent().getIntExtra(EXTRA_CONTROL_FLAGS, 0); 729 } 730 731 @Override onNewIntent(Intent intent)732 protected void onNewIntent(Intent intent) { 733 super.onNewIntent(intent); 734 mFlags = getIntent().getIntExtra(EXTRA_CONTROL_FLAGS, 0); 735 } 736 737 @Override onTopResumedActivityChanged(boolean isTopResumedActivity)738 public void onTopResumedActivityChanged(boolean isTopResumedActivity) { 739 if (!isTopResumedActivity && (mFlags & FLAG_SLOW_TOP_RESUME_RELEASE) != 0) { 740 sleep(200); 741 } else if (!isTopResumedActivity && (mFlags & FLAG_TIMEOUT_TOP_RESUME_RELEASE) != 0) { 742 sleep(2000); 743 } 744 // Intentionally moving the logging of the state change to after sleep to facilitate 745 // race condition with other activity getting top state before this releases its. 746 super.onTopResumedActivityChanged(isTopResumedActivity); 747 } 748 sleep(long millis)749 private void sleep(long millis) { 750 try { 751 Thread.sleep(millis); 752 } catch (InterruptedException e) { 753 e.printStackTrace(); 754 } 755 } 756 } 757 758 public static class DifferentAffinityActivity extends LifecycleTrackingActivity { 759 } 760 761 public static class TransitionSourceActivity extends LifecycleTrackingActivity { 762 @Override onCreate(Bundle savedInstanceState)763 protected void onCreate(Bundle savedInstanceState) { 764 super.onCreate(savedInstanceState); 765 setContentView(R.layout.transition_source_layout); 766 } 767 launchActivityWithTransition()768 void launchActivityWithTransition() { 769 final ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, 770 findViewById(R.id.transitionView), "sharedTransition"); 771 final Intent intent = new Intent(this, TransitionDestinationActivity.class); 772 startActivity(intent, options.toBundle()); 773 } 774 } 775 776 public static class TransitionDestinationActivity extends LifecycleTrackingActivity { 777 @Override onCreate(Bundle savedInstanceState)778 protected void onCreate(Bundle savedInstanceState) { 779 super.onCreate(savedInstanceState); 780 setContentView(R.layout.transition_destination_layout); 781 final Transition sharedElementEnterTransition = 782 getWindow().getSharedElementEnterTransition(); 783 sharedElementEnterTransition.addListener(new TransitionListenerAdapter() { 784 @Override 785 public void onTransitionEnd(Transition transition) { 786 super.onTransitionEnd(transition); 787 finishAfterTransition(); 788 } 789 }); 790 } 791 } 792 getComponentName(Class<? extends Activity> activity)793 static ComponentName getComponentName(Class<? extends Activity> activity) { 794 return new ComponentName(getInstrumentation().getContext(), activity); 795 } 796 moveTaskToPrimarySplitScreenAndVerify(Activity primaryActivity, Activity secondaryActivity)797 void moveTaskToPrimarySplitScreenAndVerify(Activity primaryActivity, 798 Activity secondaryActivity) throws Exception { 799 getLifecycleLog().clear(); 800 801 mWmState.computeState(secondaryActivity.getComponentName()); 802 moveActivitiesToSplitScreen(primaryActivity.getComponentName(), 803 secondaryActivity.getComponentName()); 804 805 final Class<? extends Activity> activityClass = primaryActivity.getClass(); 806 807 final List<LifecycleLog.ActivityCallback> expectedTransitions = 808 new ArrayList<LifecycleLog.ActivityCallback>( 809 LifecycleVerifier.getSplitScreenTransitionSequence(activityClass)); 810 final List<LifecycleLog.ActivityCallback> expectedTransitionForMinimizedDock = 811 LifecycleVerifier.appendMinimizedDockTransitionTrail(expectedTransitions); 812 813 final int displayWindowingMode = 814 getDisplayWindowingModeByActivity(getComponentName(activityClass)); 815 if (displayWindowingMode != WINDOWING_MODE_FULLSCREEN) { 816 // For non-fullscreen display mode, there won't be a multi-window callback. 817 expectedTransitions.removeAll(Collections.singleton(ON_MULTI_WINDOW_MODE_CHANGED)); 818 expectedTransitionForMinimizedDock.removeAll( 819 Collections.singleton(ON_MULTI_WINDOW_MODE_CHANGED)); 820 } 821 822 mLifecycleTracker.waitForActivityTransitions(activityClass, expectedTransitions); 823 LifecycleVerifier.assertSequenceMatchesOneOf( 824 activityClass, 825 getLifecycleLog(), 826 Arrays.asList(expectedTransitions, expectedTransitionForMinimizedDock), 827 "enterSplitScreen"); 828 } 829 getLaunchOptionsForFullscreen()830 final ActivityOptions getLaunchOptionsForFullscreen() { 831 final ActivityOptions launchOptions = ActivityOptions.makeBasic(); 832 launchOptions.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); 833 return launchOptions; 834 } 835 } 836