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