1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 package com.android.launcher3.ui; 17 18 import static androidx.test.InstrumentationRegistry.getInstrumentation; 19 20 import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; 21 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertTrue; 25 26 import android.content.BroadcastReceiver; 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.pm.ActivityInfo; 32 import android.content.pm.LauncherActivityInfo; 33 import android.content.pm.LauncherApps; 34 import android.content.pm.PackageInfo; 35 import android.content.pm.PackageManager; 36 import android.os.Debug; 37 import android.os.Process; 38 import android.os.RemoteException; 39 import android.os.UserHandle; 40 import android.os.UserManager; 41 import android.system.OsConstants; 42 import android.util.Log; 43 44 import androidx.test.InstrumentationRegistry; 45 import androidx.test.uiautomator.By; 46 import androidx.test.uiautomator.BySelector; 47 import androidx.test.uiautomator.UiDevice; 48 import androidx.test.uiautomator.Until; 49 50 import com.android.launcher3.Launcher; 51 import com.android.launcher3.LauncherAppState; 52 import com.android.launcher3.LauncherSettings; 53 import com.android.launcher3.LauncherState; 54 import com.android.launcher3.Utilities; 55 import com.android.launcher3.model.data.ItemInfo; 56 import com.android.launcher3.statemanager.StateManager; 57 import com.android.launcher3.tapl.LauncherInstrumentation; 58 import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType; 59 import com.android.launcher3.tapl.TestHelpers; 60 import com.android.launcher3.testcomponent.TestCommandReceiver; 61 import com.android.launcher3.testing.TestProtocol; 62 import com.android.launcher3.util.LooperExecutor; 63 import com.android.launcher3.util.PackageManagerHelper; 64 import com.android.launcher3.util.Wait; 65 import com.android.launcher3.util.WidgetUtils; 66 import com.android.launcher3.util.rule.FailureWatcher; 67 import com.android.launcher3.util.rule.LauncherActivityRule; 68 import com.android.launcher3.util.rule.ScreenRecordRule; 69 import com.android.launcher3.util.rule.ShellCommandRule; 70 import com.android.launcher3.util.rule.TestStabilityRule; 71 72 import org.junit.After; 73 import org.junit.Assert; 74 import org.junit.Before; 75 import org.junit.Rule; 76 import org.junit.rules.RuleChain; 77 import org.junit.rules.TestRule; 78 79 import java.io.IOException; 80 import java.lang.annotation.ElementType; 81 import java.lang.annotation.Retention; 82 import java.lang.annotation.RetentionPolicy; 83 import java.lang.annotation.Target; 84 import java.util.concurrent.Callable; 85 import java.util.concurrent.CountDownLatch; 86 import java.util.concurrent.TimeUnit; 87 import java.util.concurrent.TimeoutException; 88 import java.util.function.Consumer; 89 import java.util.function.Function; 90 import java.util.function.Supplier; 91 92 /** 93 * Base class for all instrumentation tests providing various utility methods. 94 */ 95 public abstract class AbstractLauncherUiTest { 96 97 public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10); 98 public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5; 99 100 public static final long DEFAULT_UI_TIMEOUT = 10000; 101 private static final String TAG = "AbstractLauncherUiTest"; 102 103 private static boolean sDumpWasGenerated = false; 104 private static boolean sActivityLeakReported = false; 105 private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; 106 107 protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR; 108 protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation()); 109 protected final LauncherInstrumentation mLauncher = new LauncherInstrumentation(); 110 protected Context mTargetContext; 111 protected String mTargetPackage; 112 private int mLauncherPid; 113 checkDetectedLeaks(LauncherInstrumentation launcher)114 public static void checkDetectedLeaks(LauncherInstrumentation launcher) { 115 if (sActivityLeakReported) return; 116 117 // Check whether activity leak detector has found leaked activities. 118 Wait.atMost(() -> getActivityLeakErrorMessage(launcher), 119 () -> { 120 launcher.forceGc(); 121 return MAIN_EXECUTOR.submit( 122 () -> launcher.noLeakedActivities()).get(); 123 }, DEFAULT_UI_TIMEOUT, launcher); 124 } 125 getActivityLeakErrorMessage(LauncherInstrumentation launcher)126 private static String getActivityLeakErrorMessage(LauncherInstrumentation launcher) { 127 sActivityLeakReported = true; 128 return "Activity leak detector has found leaked activities, " 129 + dumpHprofData(launcher) + "."; 130 } 131 dumpHprofData(LauncherInstrumentation launcher)132 public static String dumpHprofData(LauncherInstrumentation launcher) { 133 String result; 134 if (sDumpWasGenerated) { 135 Log.d("b/195319692", "dump has already been generated by another test", 136 new Exception()); 137 result = "dump has already been generated by another test"; 138 } else { 139 try { 140 final String fileName = 141 getInstrumentation().getTargetContext().getFilesDir().getPath() 142 + "/ActivityLeakHeapDump.hprof"; 143 if (TestHelpers.isInLauncherProcess()) { 144 Debug.dumpHprofData(fileName); 145 } else { 146 final UiDevice device = UiDevice.getInstance(getInstrumentation()); 147 device.executeShellCommand( 148 "am dumpheap " + device.getLauncherPackageName() + " " + fileName); 149 } 150 sDumpWasGenerated = true; 151 Log.d("b/195319692", "sDumpWasGenerated := true", new Exception()); 152 result = "saved memory dump as an artifact"; 153 } catch (Throwable e) { 154 Log.e(TAG, "dumpHprofData failed", e); 155 result = "failed to save memory dump"; 156 } 157 } 158 return result + ". Full list of activities: " + launcher.getRootedActivitiesList(); 159 } 160 AbstractLauncherUiTest()161 protected AbstractLauncherUiTest() { 162 mLauncher.enableCheckEventsForSuccessfulGestures(); 163 try { 164 mDevice.setOrientationNatural(); 165 } catch (RemoteException e) { 166 throw new RuntimeException(e); 167 } 168 if (TestHelpers.isInLauncherProcess()) { 169 Utilities.enableRunningInTestHarnessForTests(); 170 mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand( 171 TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()). 172 getString("result")); 173 mLauncher.setOnSettledStateAction( 174 containerType -> executeOnLauncher( 175 launcher -> 176 checkLauncherIntegrity(launcher, containerType))); 177 } 178 mLauncher.enableDebugTracing(); 179 // Avoid double-reporting of Launcher crashes. 180 mLauncher.setOnLauncherCrashed(() -> mLauncherPid = 0); 181 } 182 183 protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); 184 185 @Rule 186 public ShellCommandRule mDisableHeadsUpNotification = 187 ShellCommandRule.disableHeadsUpNotification(); 188 189 @Rule 190 public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule(); 191 clearPackageData(String pkg)192 protected void clearPackageData(String pkg) throws IOException, InterruptedException { 193 final CountDownLatch count = new CountDownLatch(2); 194 final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { 195 @Override 196 public void onReceive(Context context, Intent intent) { 197 count.countDown(); 198 } 199 }; 200 mTargetContext.registerReceiver(broadcastReceiver, 201 PackageManagerHelper.getPackageFilter(pkg, 202 Intent.ACTION_PACKAGE_RESTARTED, Intent.ACTION_PACKAGE_DATA_CLEARED)); 203 204 mDevice.executeShellCommand("pm clear " + pkg); 205 assertTrue(pkg + " didn't restart", count.await(10, TimeUnit.SECONDS)); 206 mTargetContext.unregisterReceiver(broadcastReceiver); 207 } 208 209 // Annotation for tests that need to be run in portrait and landscape modes. 210 @Retention(RetentionPolicy.RUNTIME) 211 @Target(ElementType.METHOD) 212 protected @interface PortraitLandscape { 213 } 214 getRulesInsideActivityMonitor()215 protected TestRule getRulesInsideActivityMonitor() { 216 final RuleChain inner = RuleChain 217 .outerRule(new PortraitLandscapeRunner(this)) 218 .around(new FailureWatcher(mDevice, mLauncher)); 219 220 return TestHelpers.isInLauncherProcess() 221 ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()) 222 .around(inner) : 223 inner; 224 } 225 226 @Rule 227 public TestRule mOrderSensitiveRules = RuleChain 228 .outerRule(new TestStabilityRule()) 229 .around(mActivityMonitor) 230 .around(getRulesInsideActivityMonitor()); 231 getDevice()232 public UiDevice getDevice() { 233 return mDevice; 234 } 235 236 @Before setUp()237 public void setUp() throws Exception { 238 mLauncher.onTestStart(); 239 Assert.assertTrue("Keyguard is visible, which is likely caused by a crash in SysUI", 240 TestHelpers.wait( 241 Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000)); 242 243 final String launcherPackageName = mDevice.getLauncherPackageName(); 244 try { 245 final Context context = InstrumentationRegistry.getContext(); 246 final PackageManager pm = context.getPackageManager(); 247 final PackageInfo launcherPackage = pm.getPackageInfo(launcherPackageName, 0); 248 249 if (!launcherPackage.versionName.equals("BuildFromAndroidStudio")) { 250 Assert.assertEquals("Launcher version doesn't match tests version", 251 pm.getPackageInfo(context.getPackageName(), 0).getLongVersionCode(), 252 launcherPackage.getLongVersionCode()); 253 } 254 } catch (PackageManager.NameNotFoundException e) { 255 throw new RuntimeException(e); 256 } 257 258 mLauncherPid = 0; 259 260 mTargetContext = InstrumentationRegistry.getTargetContext(); 261 mTargetPackage = mTargetContext.getPackageName(); 262 mLauncherPid = mLauncher.getPid(); 263 264 UserManager userManager = mTargetContext.getSystemService(UserManager.class); 265 if (userManager != null) { 266 for (UserHandle userHandle : userManager.getUserProfiles()) { 267 if (!userHandle.isSystem()) { 268 mDevice.executeShellCommand("pm remove-user " + userHandle.getIdentifier()); 269 } 270 } 271 } 272 } 273 274 @After verifyLauncherState()275 public void verifyLauncherState() { 276 try { 277 // Limits UI tests affecting tests running after them. 278 mLauncher.waitForLauncherInitialized(); 279 if (mLauncherPid != 0) { 280 assertEquals("Launcher crashed, pid mismatch:", 281 mLauncherPid, mLauncher.getPid().intValue()); 282 } 283 } finally { 284 mLauncher.onTestFinish(); 285 } 286 } 287 clearLauncherData()288 protected void clearLauncherData() { 289 mLauncher.clearLauncherData(); 290 mLauncher.waitForLauncherInitialized(); 291 } 292 293 /** 294 * Removes all icons from homescreen and hotseat. 295 */ clearHomescreen()296 public void clearHomescreen() throws Throwable { 297 LauncherSettings.Settings.call(mTargetContext.getContentResolver(), 298 LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); 299 LauncherSettings.Settings.call(mTargetContext.getContentResolver(), 300 LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); 301 resetLoaderState(); 302 } 303 resetLoaderState()304 protected void resetLoaderState() { 305 try { 306 mMainThreadExecutor.execute( 307 () -> LauncherAppState.getInstance( 308 mTargetContext).getModel().forceReload()); 309 } catch (Throwable t) { 310 throw new IllegalArgumentException(t); 311 } 312 mLauncher.waitForLauncherInitialized(); 313 } 314 315 /** 316 * Adds {@param item} on the homescreen on the 0th screen 317 */ addItemToScreen(ItemInfo item)318 protected void addItemToScreen(ItemInfo item) { 319 WidgetUtils.addItemToScreen(item, mTargetContext); 320 resetLoaderState(); 321 322 // Launch the home activity 323 mDevice.pressHome(); 324 mLauncher.waitForLauncherInitialized(); 325 } 326 327 /** 328 * Runs the callback on the UI thread and returns the result. 329 */ getOnUiThread(final Callable<T> callback)330 protected <T> T getOnUiThread(final Callable<T> callback) { 331 try { 332 return mMainThreadExecutor.submit(callback).get(DEFAULT_UI_TIMEOUT, 333 TimeUnit.MILLISECONDS); 334 } catch (TimeoutException e) { 335 Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e); 336 Process.sendSignal(Process.myPid(), OsConstants.SIGABRT); 337 throw new RuntimeException(e); 338 } catch (Throwable e) { 339 throw new RuntimeException(e); 340 } 341 } 342 getFromLauncher(Function<Launcher, T> f)343 protected <T> T getFromLauncher(Function<Launcher, T> f) { 344 if (!TestHelpers.isInLauncherProcess()) return null; 345 return getOnUiThread(() -> f.apply(mActivityMonitor.getActivity())); 346 } 347 executeOnLauncher(Consumer<Launcher> f)348 protected void executeOnLauncher(Consumer<Launcher> f) { 349 getFromLauncher(launcher -> { 350 f.accept(launcher); 351 return null; 352 }); 353 } 354 355 // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call 356 // expecting the results of that gesture because the wait can hide flakeness. waitForState(String message, Supplier<LauncherState> state)357 protected void waitForState(String message, Supplier<LauncherState> state) { 358 waitForLauncherCondition(message, 359 launcher -> launcher.getStateManager().getCurrentStableState() == state.get()); 360 } 361 362 // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call 363 // expecting the results of that gesture because the wait can hide flakeness. waitForStateTransitionToEnd(String message, Supplier<LauncherState> state)364 protected void waitForStateTransitionToEnd(String message, Supplier<LauncherState> state) { 365 waitForLauncherCondition(message, 366 launcher -> launcher.getStateManager().isInStableState(state.get()) 367 && !launcher.getStateManager().isInTransition()); 368 } 369 waitForResumed(String message)370 protected void waitForResumed(String message) { 371 waitForLauncherCondition(message, launcher -> launcher.hasBeenResumed()); 372 } 373 374 // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide 375 // flakiness. waitForLauncherCondition(String message, Function<Launcher, Boolean> condition)376 protected void waitForLauncherCondition(String 377 message, Function<Launcher, Boolean> condition) { 378 waitForLauncherCondition(message, condition, DEFAULT_ACTIVITY_TIMEOUT); 379 } 380 381 // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide 382 // flakiness. getOnceNotNull(String message, Function<Launcher, T> f)383 protected <T> T getOnceNotNull(String message, Function<Launcher, T> f) { 384 return getOnceNotNull(message, f, DEFAULT_ACTIVITY_TIMEOUT); 385 } 386 387 // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide 388 // flakiness. waitForLauncherCondition( String message, Function<Launcher, Boolean> condition, long timeout)389 protected void waitForLauncherCondition( 390 String message, Function<Launcher, Boolean> condition, long timeout) { 391 if (!TestHelpers.isInLauncherProcess()) return; 392 Wait.atMost(message, () -> getFromLauncher(condition), timeout, mLauncher); 393 } 394 395 // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide 396 // flakiness. getOnceNotNull(String message, Function<Launcher, T> f, long timeout)397 protected <T> T getOnceNotNull(String message, Function<Launcher, T> f, long timeout) { 398 if (!TestHelpers.isInLauncherProcess()) return null; 399 400 final Object[] output = new Object[1]; 401 Wait.atMost(message, () -> { 402 final Object fromLauncher = getFromLauncher(f); 403 output[0] = fromLauncher; 404 return fromLauncher != null; 405 }, timeout, mLauncher); 406 return (T) output[0]; 407 } 408 409 // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide 410 // flakiness. waitForLauncherCondition( String message, Runnable testThreadAction, Function<Launcher, Boolean> condition, long timeout)411 protected void waitForLauncherCondition( 412 String message, 413 Runnable testThreadAction, Function<Launcher, Boolean> condition, 414 long timeout) { 415 if (!TestHelpers.isInLauncherProcess()) return; 416 Wait.atMost(message, () -> { 417 testThreadAction.run(); 418 return getFromLauncher(condition); 419 }, timeout, mLauncher); 420 } 421 getSettingsApp()422 protected LauncherActivityInfo getSettingsApp() { 423 return mTargetContext.getSystemService(LauncherApps.class) 424 .getActivityList("com.android.settings", Process.myUserHandle()).get(0); 425 } 426 427 /** 428 * Broadcast receiver which blocks until the result is received. 429 */ 430 public class BlockingBroadcastReceiver extends BroadcastReceiver { 431 432 private final CountDownLatch latch = new CountDownLatch(1); 433 private Intent mIntent; 434 BlockingBroadcastReceiver(String action)435 public BlockingBroadcastReceiver(String action) { 436 mTargetContext.registerReceiver(this, new IntentFilter(action)); 437 } 438 439 @Override onReceive(Context context, Intent intent)440 public void onReceive(Context context, Intent intent) { 441 mIntent = intent; 442 latch.countDown(); 443 } 444 blockingGetIntent()445 public Intent blockingGetIntent() throws InterruptedException { 446 latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS); 447 mTargetContext.unregisterReceiver(this); 448 return mIntent; 449 } 450 blockingGetExtraIntent()451 public Intent blockingGetExtraIntent() throws InterruptedException { 452 Intent intent = blockingGetIntent(); 453 return intent == null ? null : (Intent) intent.getParcelableExtra( 454 Intent.EXTRA_INTENT); 455 } 456 } 457 startAppFast(String packageName)458 public static void startAppFast(String packageName) { 459 startIntent( 460 getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage( 461 packageName), 462 By.pkg(packageName).depth(0), 463 true /* newTask */); 464 } 465 startTestActivity(int activityNumber)466 public static void startTestActivity(int activityNumber) { 467 final String packageName = getAppPackageName(); 468 final Intent intent = getInstrumentation().getContext().getPackageManager(). 469 getLaunchIntentForPackage(packageName); 470 intent.setComponent(new ComponentName(packageName, 471 "com.android.launcher3.tests.Activity" + activityNumber)); 472 startIntent(intent, By.pkg(packageName).text("TestActivity" + activityNumber), 473 false /* newTask */); 474 } 475 startIntent(Intent intent, BySelector selector, boolean newTask)476 private static void startIntent(Intent intent, BySelector selector, boolean newTask) { 477 intent.addCategory(Intent.CATEGORY_LAUNCHER); 478 if (newTask) { 479 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 480 } else { 481 intent.addFlags( 482 Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT); 483 } 484 getInstrumentation().getTargetContext().startActivity(intent); 485 assertTrue("App didn't start: " + selector, 486 TestHelpers.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT)); 487 } 488 resolveSystemAppInfo(String category)489 public static ActivityInfo resolveSystemAppInfo(String category) { 490 return getInstrumentation().getContext().getPackageManager().resolveActivity( 491 new Intent(Intent.ACTION_MAIN).addCategory(category), 492 PackageManager.MATCH_SYSTEM_ONLY). 493 activityInfo; 494 } 495 496 resolveSystemApp(String category)497 public static String resolveSystemApp(String category) { 498 return resolveSystemAppInfo(category).packageName; 499 } 500 closeLauncherActivity()501 protected void closeLauncherActivity() { 502 // Destroy Launcher activity. 503 executeOnLauncher(launcher -> { 504 if (launcher != null) { 505 onLauncherActivityClose(launcher); 506 launcher.finish(); 507 } 508 }); 509 waitForLauncherCondition( 510 "Launcher still active", launcher -> launcher == null, DEFAULT_UI_TIMEOUT); 511 } 512 isInBackground(Launcher launcher)513 protected boolean isInBackground(Launcher launcher) { 514 return launcher == null || !launcher.hasBeenResumed(); 515 } 516 isInState(Supplier<LauncherState> state)517 protected boolean isInState(Supplier<LauncherState> state) { 518 if (!TestHelpers.isInLauncherProcess()) return true; 519 return getFromLauncher( 520 launcher -> launcher.getStateManager().getState() == state.get()); 521 } 522 getAllAppsScroll(Launcher launcher)523 protected int getAllAppsScroll(Launcher launcher) { 524 return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY(); 525 } 526 checkLauncherIntegrity( Launcher launcher, ContainerType expectedContainerType)527 private void checkLauncherIntegrity( 528 Launcher launcher, ContainerType expectedContainerType) { 529 if (launcher != null) { 530 final StateManager<LauncherState> stateManager = launcher.getStateManager(); 531 final LauncherState stableState = stateManager.getCurrentStableState(); 532 533 assertTrue("Stable state != state: " + stableState.getClass().getSimpleName() + ", " 534 + stateManager.getState().getClass().getSimpleName(), 535 stableState == stateManager.getState()); 536 537 final boolean isResumed = launcher.hasBeenResumed(); 538 final boolean isStarted = launcher.isStarted(); 539 checkLauncherState(launcher, expectedContainerType, isResumed, isStarted); 540 541 final int ordinal = stableState.ordinal; 542 543 switch (expectedContainerType) { 544 case WORKSPACE: 545 case WIDGETS: { 546 assertTrue( 547 "Launcher is not resumed in state: " + expectedContainerType, 548 isResumed); 549 assertTrue(TestProtocol.stateOrdinalToString(ordinal), 550 ordinal == TestProtocol.NORMAL_STATE_ORDINAL); 551 break; 552 } 553 case ALL_APPS: { 554 assertTrue( 555 "Launcher is not resumed in state: " + expectedContainerType, 556 isResumed); 557 assertTrue(TestProtocol.stateOrdinalToString(ordinal), 558 ordinal == TestProtocol.ALL_APPS_STATE_ORDINAL); 559 break; 560 } 561 case OVERVIEW: { 562 checkLauncherStateInOverview(launcher, expectedContainerType, isStarted, 563 isResumed); 564 assertTrue(TestProtocol.stateOrdinalToString(ordinal), 565 ordinal == TestProtocol.OVERVIEW_STATE_ORDINAL); 566 break; 567 } 568 case BACKGROUND: { 569 assertTrue("Launcher is resumed in state: " + expectedContainerType, 570 !isResumed); 571 assertTrue(TestProtocol.stateOrdinalToString(ordinal), 572 ordinal == TestProtocol.NORMAL_STATE_ORDINAL); 573 break; 574 } 575 default: 576 throw new IllegalArgumentException( 577 "Illegal container: " + expectedContainerType); 578 } 579 } else { 580 assertTrue( 581 "Container type is not BACKGROUND or FALLBACK_OVERVIEW: " 582 + expectedContainerType, 583 expectedContainerType == ContainerType.BACKGROUND || 584 expectedContainerType == ContainerType.FALLBACK_OVERVIEW); 585 } 586 } 587 checkLauncherState(Launcher launcher, ContainerType expectedContainerType, boolean isResumed, boolean isStarted)588 protected void checkLauncherState(Launcher launcher, ContainerType expectedContainerType, 589 boolean isResumed, boolean isStarted) { 590 assertTrue("hasBeenResumed() != isStarted(), hasBeenResumed(): " + isResumed, 591 isResumed == isStarted); 592 assertTrue("hasBeenResumed() != isUserActive(), hasBeenResumed(): " + isResumed, 593 isResumed == launcher.isUserActive()); 594 } 595 checkLauncherStateInOverview(Launcher launcher, ContainerType expectedContainerType, boolean isStarted, boolean isResumed)596 protected void checkLauncherStateInOverview(Launcher launcher, 597 ContainerType expectedContainerType, boolean isStarted, boolean isResumed) { 598 assertTrue("Launcher is not resumed in state: " + expectedContainerType, 599 isResumed); 600 } 601 onLauncherActivityClose(Launcher launcher)602 protected void onLauncherActivityClose(Launcher launcher) { 603 } 604 } 605