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