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