1 package android.server.am.lifecycle; 2 3 import static android.server.am.StateLogger.log; 4 import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_ACTIVITY_RESULT; 5 import static android.server.am.lifecycle.LifecycleLog.ActivityCallback 6 .ON_MULTI_WINDOW_MODE_CHANGED; 7 import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_NEW_INTENT; 8 import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_PAUSE; 9 import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_POST_CREATE; 10 import static android.server.am.lifecycle.LifecycleLog.ActivityCallback.ON_STOP; 11 12 import android.annotation.Nullable; 13 import android.app.Activity; 14 import android.content.ComponentName; 15 import android.content.Intent; 16 import android.content.pm.ActivityInfo; 17 import android.content.res.Configuration; 18 import android.os.Bundle; 19 import android.os.Handler; 20 import android.server.am.ActivityManagerTestBase; 21 import android.server.am.lifecycle.LifecycleLog.ActivityCallback; 22 import android.support.test.InstrumentationRegistry; 23 import android.support.test.rule.ActivityTestRule; 24 import android.support.test.runner.lifecycle.ActivityLifecycleMonitor; 25 import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; 26 import android.util.Pair; 27 28 import org.junit.After; 29 import org.junit.Before; 30 31 import java.util.List; 32 33 /** Base class for device-side tests that verify correct activity lifecycle transitions. */ 34 public class ActivityLifecycleClientTestBase extends ActivityManagerTestBase { 35 36 static final String EXTRA_RECREATE = "recreate"; 37 static final String EXTRA_FINISH_IN_ON_RESUME = "finish_in_on_resume"; 38 static final String EXTRA_FINISH_AFTER_RESUME = "finish_after_resume"; 39 40 static final ComponentName CALLBACK_TRACKING_ACTIVITY = 41 getComponentName(CallbackTrackingActivity.class); 42 43 static final ComponentName CONFIG_CHANGE_HANDLING_ACTIVITY = 44 getComponentName(ConfigChangeHandlingActivity.class); 45 46 final ActivityTestRule mFirstActivityTestRule = new ActivityTestRule(FirstActivity.class, 47 true /* initialTouchMode */, false /* launchActivity */); 48 49 final ActivityTestRule mSecondActivityTestRule = new ActivityTestRule(SecondActivity.class, 50 true /* initialTouchMode */, false /* launchActivity */); 51 52 final ActivityTestRule mTranslucentActivityTestRule = new ActivityTestRule( 53 TranslucentActivity.class, true /* initialTouchMode */, false /* launchActivity */); 54 55 final ActivityTestRule mSecondTranslucentActivityTestRule = new ActivityTestRule( 56 SecondTranslucentActivity.class, true /* initialTouchMode */, 57 false /* launchActivity */); 58 59 final ActivityTestRule mLaunchForResultActivityTestRule = new ActivityTestRule( 60 LaunchForResultActivity.class, true /* initialTouchMode */, false /* launchActivity */); 61 62 final ActivityTestRule mCallbackTrackingActivityTestRule = new ActivityTestRule( 63 CallbackTrackingActivity.class, true /* initialTouchMode */, 64 false /* launchActivity */); 65 66 final ActivityTestRule mSingleTopActivityTestRule = new ActivityTestRule( 67 SingleTopActivity.class, true /* initialTouchMode */, false /* launchActivity */); 68 69 final ActivityTestRule mConfigChangeHandlingActivityTestRule = new ActivityTestRule( 70 ConfigChangeHandlingActivity.class, true /* initialTouchMode */, 71 false /* launchActivity */); 72 73 private final ActivityLifecycleMonitor mLifecycleMonitor = ActivityLifecycleMonitorRegistry 74 .getInstance(); 75 private static LifecycleLog mLifecycleLog; 76 private LifecycleTracker mLifecycleTracker; 77 78 @Before 79 @Override 80 public void setUp() throws Exception { 81 super.setUp(); 82 // Log transitions for all activities that belong to this app. 83 mLifecycleLog = new LifecycleLog(); 84 mLifecycleMonitor.addLifecycleCallback(mLifecycleLog); 85 86 // Track transitions and allow waiting for pending activity states. 87 mLifecycleTracker = new LifecycleTracker(mLifecycleLog); 88 mLifecycleMonitor.addLifecycleCallback(mLifecycleTracker); 89 } 90 91 @After 92 @Override 93 public void tearDown() throws Exception { 94 mLifecycleMonitor.removeLifecycleCallback(mLifecycleLog); 95 mLifecycleMonitor.removeLifecycleCallback(mLifecycleTracker); 96 super.tearDown(); 97 } 98 99 /** Launch an activity given a class. */ 100 protected Activity launchActivity(Class<? extends Activity> activityClass) { 101 final Intent intent = new Intent(InstrumentationRegistry.getTargetContext(), activityClass); 102 return InstrumentationRegistry.getInstrumentation().startActivitySync(intent); 103 } 104 105 /** 106 * Blocking call that will wait for activities to reach expected states with timeout. 107 */ 108 @SafeVarargs 109 final void waitAndAssertActivityStates(Pair<Activity, ActivityCallback>... activityCallbacks) { 110 log("Start waitAndAssertActivityCallbacks"); 111 mLifecycleTracker.waitAndAssertActivityStates(activityCallbacks); 112 } 113 114 /** 115 * Blocking call that will wait for activities to perform the expected sequence of transitions. 116 * @see LifecycleTracker#waitForActivityTransitions(Class, List) 117 */ 118 final void waitForActivityTransitions(Class<? extends Activity> activityClass, 119 List<ActivityCallback> expectedTransitions) { 120 log("Start waitAndAssertActivityTransition"); 121 mLifecycleTracker.waitForActivityTransitions(activityClass, expectedTransitions); 122 } 123 124 LifecycleLog getLifecycleLog() { 125 return mLifecycleLog; 126 } 127 128 static Pair<Activity, ActivityCallback> state(Activity activity, ActivityCallback stage) { 129 return new Pair<>(activity, stage); 130 } 131 132 /** 133 * Returns a pair of the activity and the state it should be in based on the configuration of 134 * occludingActivity. 135 */ 136 static Pair<Activity, ActivityCallback> occludedActivityState( 137 Activity activity, Activity occludingActivity) { 138 return occludedActivityState(activity, isTranslucent(occludingActivity)); 139 } 140 141 /** 142 * Returns a pair of the activity and the state it should be in based on 143 * occludingActivityIsTranslucent. 144 */ 145 static Pair<Activity, ActivityCallback> occludedActivityState( 146 Activity activity, boolean occludingActivityIsTranslucent) { 147 // Activities behind a translucent activity should be in the paused state since they are 148 // still visible. Otherwise, they should be in the stopped state. 149 return new Pair<>(activity, occludedActivityState(occludingActivityIsTranslucent)); 150 } 151 152 static ActivityCallback occludedActivityState(boolean occludingActivityIsTranslucent) { 153 return occludingActivityIsTranslucent ? ON_PAUSE : ON_STOP; 154 } 155 156 /** Returns true if the input activity is translucent. */ 157 static boolean isTranslucent(Activity activity) { 158 return ActivityInfo.isTranslucentOrFloating(activity.getWindow().getWindowStyle()); 159 } 160 161 // Test activity 162 public static class FirstActivity extends Activity { 163 } 164 165 // Test activity 166 public static class SecondActivity extends Activity { 167 } 168 169 // Translucent test activity 170 public static class TranslucentActivity extends Activity { 171 } 172 173 // Translucent test activity 174 public static class SecondTranslucentActivity extends Activity { 175 } 176 177 /** 178 * Base activity that records callbacks other then main lifecycle transitions. 179 */ 180 public static class CallbackTrackingActivity extends Activity { 181 @Override 182 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 183 super.onActivityResult(requestCode, resultCode, data); 184 mLifecycleLog.onActivityCallback(this, ON_ACTIVITY_RESULT); 185 } 186 187 @Override 188 protected void onPostCreate(@Nullable Bundle savedInstanceState) { 189 super.onPostCreate(savedInstanceState); 190 mLifecycleLog.onActivityCallback(this, ON_POST_CREATE); 191 } 192 193 @Override 194 protected void onNewIntent(Intent intent) { 195 super.onNewIntent(intent); 196 mLifecycleLog.onActivityCallback(this, ON_NEW_INTENT); 197 } 198 199 @Override 200 public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) { 201 mLifecycleLog.onActivityCallback(this, ON_MULTI_WINDOW_MODE_CHANGED); 202 } 203 } 204 205 /** 206 * Test activity that launches {@link ResultActivity} for result. 207 */ 208 public static class LaunchForResultActivity extends CallbackTrackingActivity { 209 210 @Override 211 protected void onCreate(Bundle savedInstanceState) { 212 super.onCreate(savedInstanceState); 213 startForResult(); 214 } 215 216 private void startForResult() { 217 final Intent intent = new Intent(this, ResultActivity.class); 218 intent.putExtras(getIntent()); 219 startActivityForResult(intent, 1 /* requestCode */); 220 } 221 } 222 223 /** Test activity that is started for result and finishes itself in ON_RESUME. */ 224 public static class ResultActivity extends Activity { 225 @Override 226 protected void onResume() { 227 super.onResume(); 228 setResult(RESULT_OK); 229 final Intent intent = getIntent(); 230 if (intent.getBooleanExtra(EXTRA_FINISH_IN_ON_RESUME, false)) { 231 finish(); 232 } else if (intent.getBooleanExtra(EXTRA_FINISH_AFTER_RESUME, false)) { 233 new Handler().postDelayed(() -> finish(), 2000); 234 } 235 } 236 } 237 238 /** Test activity that can call {@link Activity#recreate()} if requested in a new intent. */ 239 public static class SingleTopActivity extends CallbackTrackingActivity { 240 241 @Override 242 protected void onNewIntent(Intent intent) { 243 super.onNewIntent(intent); 244 if (intent != null && intent.getBooleanExtra(EXTRA_RECREATE, false)) { 245 recreate(); 246 } 247 } 248 } 249 250 // Config change handling activity 251 public static class ConfigChangeHandlingActivity extends CallbackTrackingActivity { 252 } 253 254 static ComponentName getComponentName(Class<? extends Activity> activity) { 255 return new ComponentName(InstrumentationRegistry.getContext(), activity); 256 } 257 } 258