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