• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package android.server.wm;
18 
19 import static android.app.AppOpsManager.MODE_ALLOWED;
20 import static android.app.AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW;
21 import static android.app.Instrumentation.ActivityMonitor;
22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
24 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
25 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
26 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
27 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
28 import static android.content.Intent.ACTION_MAIN;
29 import static android.content.Intent.CATEGORY_HOME;
30 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
31 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
32 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
33 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
34 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
35 import static android.content.pm.PackageManager.DONT_KILL_APP;
36 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
37 import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
38 import static android.content.pm.PackageManager.FEATURE_EMBEDDED;
39 import static android.content.pm.PackageManager.FEATURE_EXPANDED_PICTURE_IN_PICTURE;
40 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
41 import static android.content.pm.PackageManager.FEATURE_INPUT_METHODS;
42 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
43 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
44 import static android.content.pm.PackageManager.FEATURE_SCREEN_LANDSCAPE;
45 import static android.content.pm.PackageManager.FEATURE_SCREEN_PORTRAIT;
46 import static android.content.pm.PackageManager.FEATURE_SECURE_LOCK_SCREEN;
47 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
48 import static android.content.pm.PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE;
49 import static android.content.pm.PackageManager.FEATURE_WATCH;
50 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
51 import static android.os.UserHandle.USER_SYSTEM;
52 import static android.server.wm.ActivityLauncher.KEY_ACTIVITY_TYPE;
53 import static android.server.wm.ActivityLauncher.KEY_DISPLAY_ID;
54 import static android.server.wm.ActivityLauncher.KEY_INTENT_EXTRAS;
55 import static android.server.wm.ActivityLauncher.KEY_INTENT_FLAGS;
56 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
57 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_TASK_BEHIND;
58 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_TO_SIDE;
59 import static android.server.wm.ActivityLauncher.KEY_MULTIPLE_INSTANCES;
60 import static android.server.wm.ActivityLauncher.KEY_MULTIPLE_TASK;
61 import static android.server.wm.ActivityLauncher.KEY_NEW_TASK;
62 import static android.server.wm.ActivityLauncher.KEY_RANDOM_DATA;
63 import static android.server.wm.ActivityLauncher.KEY_REORDER_TO_FRONT;
64 import static android.server.wm.ActivityLauncher.KEY_SUPPRESS_EXCEPTIONS;
65 import static android.server.wm.ActivityLauncher.KEY_TARGET_COMPONENT;
66 import static android.server.wm.ActivityLauncher.KEY_USE_APPLICATION_CONTEXT;
67 import static android.server.wm.ActivityLauncher.KEY_WINDOWING_MODE;
68 import static android.server.wm.ActivityLauncher.launchActivityFromExtras;
69 import static android.server.wm.CommandSession.KEY_FORWARD;
70 import static android.server.wm.ComponentNameUtils.getActivityName;
71 import static android.server.wm.ComponentNameUtils.getLogTag;
72 import static android.server.wm.StateLogger.log;
73 import static android.server.wm.StateLogger.logE;
74 import static android.server.wm.UiDeviceUtils.pressBackButton;
75 import static android.server.wm.UiDeviceUtils.pressEnterButton;
76 import static android.server.wm.UiDeviceUtils.pressHomeButton;
77 import static android.server.wm.UiDeviceUtils.pressSleepButton;
78 import static android.server.wm.UiDeviceUtils.pressUnlockButton;
79 import static android.server.wm.UiDeviceUtils.pressWakeupButton;
80 import static android.server.wm.UiDeviceUtils.waitForDeviceIdle;
81 import static android.server.wm.WindowManagerState.STATE_RESUMED;
82 import static android.server.wm.WindowManagerState.STATE_STOPPED;
83 import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
84 import static android.server.wm.app.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
85 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_BROADCAST_ORIENTATION;
86 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_CUTOUT_EXISTS;
87 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD;
88 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD_METHOD;
89 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
90 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_MOVE_BROADCAST_TO_BACK;
91 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
92 import static android.server.wm.app.Components.LaunchingActivity.KEY_FINISH_BEFORE_LAUNCH;
93 import static android.server.wm.app.Components.PipActivity.ACTION_CHANGE_ASPECT_RATIO;
94 import static android.server.wm.app.Components.PipActivity.ACTION_ENTER_PIP;
95 import static android.server.wm.app.Components.PipActivity.ACTION_EXPAND_PIP;
96 import static android.server.wm.app.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION;
97 import static android.server.wm.app.Components.PipActivity.ACTION_UPDATE_PIP_STATE;
98 import static android.server.wm.app.Components.PipActivity.EXTRA_PIP_ORIENTATION;
99 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_DENOMINATOR;
100 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_NUMERATOR;
101 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR;
102 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR;
103 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_PIP_CALLBACK;
104 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_PIP_STASHED;
105 import static android.server.wm.app.Components.TEST_ACTIVITY;
106 import static android.server.wm.second.Components.SECOND_ACTIVITY;
107 import static android.server.wm.third.Components.THIRD_ACTIVITY;
108 import static android.view.Display.DEFAULT_DISPLAY;
109 import static android.view.Display.INVALID_DISPLAY;
110 import static android.view.Surface.ROTATION_0;
111 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
112 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
113 
114 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
115 
116 import static org.junit.Assert.assertEquals;
117 import static org.junit.Assert.assertNotNull;
118 import static org.junit.Assert.assertTrue;
119 import static org.junit.Assert.fail;
120 import static org.junit.Assume.assumeTrue;
121 
122 import static java.lang.Integer.toHexString;
123 
124 import android.accessibilityservice.AccessibilityService;
125 import android.app.Activity;
126 import android.app.ActivityManager;
127 import android.app.ActivityOptions;
128 import android.app.ActivityTaskManager;
129 import android.app.Instrumentation;
130 import android.app.KeyguardManager;
131 import android.app.WindowConfiguration;
132 import android.content.ComponentName;
133 import android.content.ContentResolver;
134 import android.content.Context;
135 import android.content.Intent;
136 import android.content.pm.PackageManager;
137 import android.content.pm.ResolveInfo;
138 import android.content.res.Resources;
139 import android.database.ContentObserver;
140 import android.graphics.Bitmap;
141 import android.graphics.Rect;
142 import android.hardware.display.AmbientDisplayConfiguration;
143 import android.hardware.display.DisplayManager;
144 import android.os.Bundle;
145 import android.os.Handler;
146 import android.os.HandlerThread;
147 import android.os.PowerManager;
148 import android.os.RemoteCallback;
149 import android.os.SystemClock;
150 import android.os.SystemProperties;
151 import android.provider.Settings;
152 import android.server.wm.CommandSession.ActivityCallback;
153 import android.server.wm.CommandSession.ActivitySession;
154 import android.server.wm.CommandSession.ActivitySessionClient;
155 import android.server.wm.CommandSession.ConfigInfo;
156 import android.server.wm.CommandSession.LaunchInjector;
157 import android.server.wm.CommandSession.LaunchProxy;
158 import android.server.wm.CommandSession.SizeInfo;
159 import android.server.wm.TestJournalProvider.TestJournalContainer;
160 import android.server.wm.WindowManagerState.Task;
161 import android.server.wm.WindowManagerState.WindowState;
162 import android.server.wm.settings.SettingsSession;
163 import android.util.DisplayMetrics;
164 import android.util.EventLog;
165 import android.util.EventLog.Event;
166 import android.util.Size;
167 import android.view.Display;
168 import android.view.View;
169 import android.view.WindowManager;
170 
171 import androidx.annotation.NonNull;
172 import androidx.annotation.Nullable;
173 import androidx.test.core.app.ApplicationProvider;
174 import androidx.test.ext.junit.rules.ActivityScenarioRule;
175 
176 import com.android.compatibility.common.util.AppOpsUtils;
177 import com.android.compatibility.common.util.SystemUtil;
178 
179 import org.junit.Before;
180 import org.junit.Rule;
181 import org.junit.rules.ErrorCollector;
182 import org.junit.rules.RuleChain;
183 import org.junit.rules.TestRule;
184 import org.junit.runner.Description;
185 import org.junit.runners.model.Statement;
186 
187 import java.io.IOException;
188 import java.util.ArrayList;
189 import java.util.Arrays;
190 import java.util.Collections;
191 import java.util.Iterator;
192 import java.util.List;
193 import java.util.Objects;
194 import java.util.UUID;
195 import java.util.concurrent.CompletableFuture;
196 import java.util.concurrent.TimeUnit;
197 import java.util.concurrent.atomic.AtomicBoolean;
198 import java.util.function.BooleanSupplier;
199 import java.util.function.Consumer;
200 import java.util.regex.Matcher;
201 import java.util.regex.Pattern;
202 
203 public abstract class ActivityManagerTestBase {
204     private static final boolean PRETEND_DEVICE_SUPPORTS_PIP = false;
205     private static final boolean PRETEND_DEVICE_SUPPORTS_FREEFORM = false;
206     private static final String LOG_SEPARATOR = "LOG_SEPARATOR";
207     // Use one of the test tags as a separator
208     private static final int EVENT_LOG_SEPARATOR_TAG = 42;
209 
210     protected static final int[] ALL_ACTIVITY_TYPE_BUT_HOME = {
211             ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS,
212             ACTIVITY_TYPE_UNDEFINED
213     };
214 
215     private static final String TEST_PACKAGE = TEST_ACTIVITY.getPackageName();
216     private static final String SECOND_TEST_PACKAGE = SECOND_ACTIVITY.getPackageName();
217     private static final String THIRD_TEST_PACKAGE = THIRD_ACTIVITY.getPackageName();
218     private static final List<String> TEST_PACKAGES;
219 
220     static {
221         final List<String> testPackages = new ArrayList<>();
222         testPackages.add(TEST_PACKAGE);
223         testPackages.add(SECOND_TEST_PACKAGE);
224         testPackages.add(THIRD_TEST_PACKAGE);
225         testPackages.add("android.server.wm.cts");
226         testPackages.add("android.server.wm.jetpack");
227         testPackages.add("android.server.wm.jetpack.second");
228         TEST_PACKAGES = Collections.unmodifiableList(testPackages);
229     }
230 
231     protected static final String AM_START_HOME_ACTIVITY_COMMAND =
232             "am start -a android.intent.action.MAIN -c android.intent.category.HOME";
233 
234     protected static final String MSG_NO_MOCK_IME =
235             "MockIme cannot be used for devices that do not support installable IMEs";
236 
237     private static final String AM_BROADCAST_CLOSE_SYSTEM_DIALOGS =
238             "am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS";
239 
240     protected static final String LOCK_CREDENTIAL = "1234";
241 
242     private static final int UI_MODE_TYPE_MASK = 0x0f;
243     private static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
244 
245     public static final boolean ENABLE_SHELL_TRANSITIONS =
246             SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
247 
248     private static Boolean sHasHomeScreen = null;
249     private static Boolean sSupportsSystemDecorsOnSecondaryDisplays = null;
250     private static Boolean sSupportsInsecureLockScreen = null;
251     private static Boolean sIsAssistantOnTop = null;
252     private static Boolean sIsTablet = null;
253     private static Boolean sDismissDreamOnActivityStart = null;
254     private static boolean sIllegalTaskStateFound;
255 
256     protected static final int INVALID_DEVICE_ROTATION = -1;
257 
258     protected final Instrumentation mInstrumentation = getInstrumentation();
259     protected final Context mContext = getInstrumentation().getContext();
260     protected final ActivityManager mAm = mContext.getSystemService(ActivityManager.class);
261     protected final ActivityTaskManager mAtm = mContext.getSystemService(ActivityTaskManager.class);
262     protected final DisplayManager mDm = mContext.getSystemService(DisplayManager.class);
263     protected final WindowManager mWm = mContext.getSystemService(WindowManager.class);
264     protected final KeyguardManager mKm = mContext.getSystemService(KeyguardManager.class);
265 
266     /** The tracker to manage objects (especially {@link AutoCloseable}) in a test method. */
267     protected final ObjectTracker mObjectTracker = new ObjectTracker();
268 
269     /** The last rule to handle all errors. */
270     private final ErrorCollector mPostAssertionRule = new PostAssertionRule();
271 
272     /** The necessary procedures of set up and tear down. */
273     @Rule
274     public final TestRule mBaseRule = RuleChain.outerRule(mPostAssertionRule)
275             .around(new WrapperRule(null /* before */, this::tearDownBase));
276 
277     /**
278      * Whether to wait for the rotation to be stable state after testing. It can be set if the
279      * display rotation may be changed by test.
280      */
281     protected boolean mWaitForRotationOnTearDown;
282 
283     /** Indicate to wait for all non-home activities to be destroyed when test finished. */
284     protected boolean mShouldWaitForAllNonHomeActivitiesToDestroyed = false;
285 
286     /**
287      * @return the am command to start the given activity with the following extra key/value pairs.
288      * {@param extras} a list of {@link CliIntentExtra} representing a generic intent extra
289      */
290     // TODO: Make this more generic, for instance accepting flags or extras of other types.
getAmStartCmd(final ComponentName activityName, final CliIntentExtra... extras)291     protected static String getAmStartCmd(final ComponentName activityName,
292             final CliIntentExtra... extras) {
293         return getAmStartCmdInternal(getActivityName(activityName), extras);
294     }
295 
getAmStartCmdInternal(final String activityName, final CliIntentExtra... extras)296     private static String getAmStartCmdInternal(final String activityName,
297             final CliIntentExtra... extras) {
298         return appendKeyValuePairs(
299                 new StringBuilder("am start -n ").append(activityName),
300                 extras);
301     }
302 
appendKeyValuePairs( final StringBuilder cmd, final CliIntentExtra... extras)303     private static String appendKeyValuePairs(
304             final StringBuilder cmd, final CliIntentExtra... extras) {
305         for (int i = 0; i < extras.length; i++) {
306             extras[i].appendTo(cmd);
307         }
308         return cmd.toString();
309     }
310 
getAmStartCmd(final ComponentName activityName, final int displayId, final CliIntentExtra... extras)311     protected static String getAmStartCmd(final ComponentName activityName, final int displayId,
312             final CliIntentExtra... extras) {
313         return getAmStartCmdInternal(getActivityName(activityName), displayId, extras);
314     }
315 
getAmStartCmdInternal(final String activityName, final int displayId, final CliIntentExtra... extras)316     private static String getAmStartCmdInternal(final String activityName, final int displayId,
317             final CliIntentExtra... extras) {
318         return appendKeyValuePairs(
319                 new StringBuilder("am start -n ")
320                         .append(activityName)
321                         .append(" -f 0x")
322                         .append(toHexString(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK))
323                         .append(" --display ")
324                         .append(displayId),
325                 extras);
326     }
327 
getAmStartCmdInNewTask(final ComponentName activityName)328     protected static String getAmStartCmdInNewTask(final ComponentName activityName) {
329         return "am start -n " + getActivityName(activityName) + " -f 0x18000000";
330     }
331 
getAmStartCmdWithData(final ComponentName activityName, String data)332     protected static String getAmStartCmdWithData(final ComponentName activityName, String data) {
333         return "am start -n " + getActivityName(activityName) + " -d " + data;
334     }
335 
getAmStartCmdOverHome(final ComponentName activityName)336     protected static String getAmStartCmdOverHome(final ComponentName activityName) {
337         return "am start --activity-task-on-home -n " + getActivityName(activityName);
338     }
339 
getAmStartCmdWithDismissKeyguard( final ComponentName activityName)340     protected static String getAmStartCmdWithDismissKeyguard(
341             final ComponentName activityName) {
342         return "am start --dismiss-keyguard -n " + getActivityName(activityName);
343     }
344 
getAmStartCmdWithNoUserAction(final ComponentName activityName, final CliIntentExtra... extras)345     protected static String getAmStartCmdWithNoUserAction(final ComponentName activityName,
346             final CliIntentExtra... extras) {
347         return appendKeyValuePairs(
348                 new StringBuilder("am start -n ")
349                         .append(getActivityName(activityName))
350                         .append(" -f 0x")
351                         .append(toHexString(FLAG_ACTIVITY_NO_USER_ACTION)),
352                 extras);
353     }
354 
getAmStartCmdWithWindowingMode( final ComponentName activityName, int windowingMode)355     protected static String getAmStartCmdWithWindowingMode(
356             final ComponentName activityName, int windowingMode) {
357         return getAmStartCmdInNewTask(activityName) + " --windowingMode " + windowingMode;
358     }
359 
360     protected WindowManagerStateHelper mWmState = new WindowManagerStateHelper();
361     protected TouchHelper mTouchHelper = new TouchHelper(mInstrumentation, mWmState);
362     // Initialized in setUp to execute with proper permission, such as MANAGE_ACTIVITY_TASKS
363     public TestTaskOrganizer mTaskOrganizer;
364 
getWmState()365     public WindowManagerStateHelper getWmState() {
366         return mWmState;
367     }
368 
369     protected BroadcastActionTrigger mBroadcastActionTrigger = new BroadcastActionTrigger();
370 
371     /** Runs a runnable with shell permissions. These can be nested. */
runWithShellPermission(Runnable runnable)372     protected void runWithShellPermission(Runnable runnable) {
373         NestedShellPermission.run(runnable);
374     }
375     /**
376      * Returns true if the activity is shown before timeout.
377      */
waitForActivityFocused(int timeoutMs, ComponentName componentName)378     protected boolean waitForActivityFocused(int timeoutMs, ComponentName componentName) {
379         long endTime = System.currentTimeMillis() + timeoutMs;
380         while (endTime > System.currentTimeMillis()) {
381             mWmState.computeState();
382             if (mWmState.hasActivityState(componentName, STATE_RESUMED)) {
383                 SystemClock.sleep(200);
384                 mWmState.computeState();
385                 break;
386             }
387             SystemClock.sleep(200);
388             mWmState.computeState();
389         }
390         return getActivityName(componentName).equals(mWmState.getFocusedActivity());
391     }
392 
393     /**
394      * Helper class to process test actions by broadcast.
395      */
396     protected class BroadcastActionTrigger {
397 
createIntentWithAction(String broadcastAction)398         private Intent createIntentWithAction(String broadcastAction) {
399             return new Intent(broadcastAction)
400                     .setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
401         }
402 
doAction(String broadcastAction)403         void doAction(String broadcastAction) {
404             mContext.sendBroadcast(createIntentWithAction(broadcastAction));
405         }
406 
finishBroadcastReceiverActivity()407         void finishBroadcastReceiverActivity() {
408             mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
409                     .putExtra(EXTRA_FINISH_BROADCAST, true));
410         }
411 
launchActivityNewTask(String launchComponent)412         void launchActivityNewTask(String launchComponent) {
413             mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
414                     .putExtra(KEY_LAUNCH_ACTIVITY, true)
415                     .putExtra(KEY_NEW_TASK, true)
416                     .putExtra(KEY_TARGET_COMPONENT, launchComponent));
417         }
418 
moveTopTaskToBack()419         void moveTopTaskToBack() {
420             mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
421                     .putExtra(EXTRA_MOVE_BROADCAST_TO_BACK, true));
422         }
423 
requestOrientation(int orientation)424         void requestOrientation(int orientation) {
425             mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
426                     .putExtra(EXTRA_BROADCAST_ORIENTATION, orientation));
427         }
428 
dismissKeyguardByFlag()429         void dismissKeyguardByFlag() {
430             mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
431                     .putExtra(EXTRA_DISMISS_KEYGUARD, true));
432         }
433 
dismissKeyguardByMethod()434         void dismissKeyguardByMethod() {
435             mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
436                     .putExtra(EXTRA_DISMISS_KEYGUARD_METHOD, true));
437         }
438 
enterPipAndWait()439         void enterPipAndWait() {
440             try {
441                 final CompletableFuture<Boolean> future = new CompletableFuture<>();
442                 final RemoteCallback remoteCallback = new RemoteCallback(
443                         (Bundle result) -> future.complete(true));
444                 mContext.sendBroadcast(createIntentWithAction(ACTION_ENTER_PIP)
445                         .putExtra(EXTRA_SET_PIP_CALLBACK, remoteCallback));
446                 assertTrue(future.get(5000, TimeUnit.MILLISECONDS));
447             } catch (Exception e) {
448                 logE("enterPipAndWait failed", e);
449             }
450         }
451 
expandPip()452         void expandPip() {
453             mContext.sendBroadcast(createIntentWithAction(ACTION_EXPAND_PIP));
454         }
455 
expandPipWithAspectRatio(String extraNum, String extraDenom)456         void expandPipWithAspectRatio(String extraNum, String extraDenom) {
457             mContext.sendBroadcast(createIntentWithAction(ACTION_EXPAND_PIP)
458                     .putExtra(EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR, extraNum)
459                     .putExtra(EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR, extraDenom));
460         }
461 
sendPipStateUpdate(RemoteCallback callback, boolean stashed)462         void sendPipStateUpdate(RemoteCallback callback, boolean stashed) {
463             mContext.sendBroadcast(createIntentWithAction(ACTION_UPDATE_PIP_STATE)
464                     .putExtra(EXTRA_SET_PIP_CALLBACK, callback)
465                     .putExtra(EXTRA_SET_PIP_STASHED, stashed));
466         }
467 
requestOrientationForPip(int orientation)468         void requestOrientationForPip(int orientation) {
469             mContext.sendBroadcast(createIntentWithAction(ACTION_SET_REQUESTED_ORIENTATION)
470                     .putExtra(EXTRA_PIP_ORIENTATION, String.valueOf(orientation)));
471         }
472 
changeAspectRatio(int numerator, int denominator)473         void changeAspectRatio(int numerator, int denominator) {
474             mContext.sendBroadcast(createIntentWithAction(ACTION_CHANGE_ASPECT_RATIO)
475                     .putExtra(EXTRA_SET_ASPECT_RATIO_NUMERATOR, Integer.toString(numerator))
476                     .putExtra(EXTRA_SET_ASPECT_RATIO_DENOMINATOR, Integer.toString(denominator)));
477         }
478     }
479 
480     /**
481      * Helper class to launch / close test activity by instrumentation way.
482      */
483     protected class TestActivitySession<T extends Activity> implements AutoCloseable {
484         private T mTestActivity;
485         boolean mFinishAfterClose;
486         private static final int ACTIVITY_LAUNCH_TIMEOUT = 10000;
487         private static final int WAIT_SLICE = 50;
488 
489         /**
490          * Launches an {@link Activity} on a target display synchronously.
491          * @param activityClass The {@link Activity} class to be launched
492          * @param displayId ID of the target display
493          */
launchTestActivityOnDisplaySync(Class<T> activityClass, int displayId)494         public void launchTestActivityOnDisplaySync(Class<T> activityClass, int displayId) {
495             launchTestActivityOnDisplaySync(activityClass, displayId, WINDOWING_MODE_UNDEFINED);
496         }
497 
498         /**
499          * Launches an {@link Activity} on a target display synchronously.
500          *
501          * @param activityClass The {@link Activity} class to be launched
502          * @param displayId ID of the target display
503          * @param windowingMode Windowing mode at launch
504          */
launchTestActivityOnDisplaySync( Class<T> activityClass, int displayId, int windowingMode)505         void launchTestActivityOnDisplaySync(
506                 Class<T> activityClass, int displayId, int windowingMode) {
507             final Intent intent = new Intent(mContext, activityClass)
508                     .addFlags(FLAG_ACTIVITY_NEW_TASK);
509             final String className = intent.getComponent().getClassName();
510             launchTestActivityOnDisplaySync(className, intent, displayId, windowingMode);
511         }
512 
513         /**
514          * Launches an {@link Activity} synchronously on a target display. The class name needs to
515          * be provided either implicitly through the {@link Intent} or explicitly as a parameter
516          *
517          * @param className Optional class name of expected activity
518          * @param intent Intent to launch an activity
519          * @param displayId ID for the target display
520          */
launchTestActivityOnDisplaySync(@ullable String className, Intent intent, int displayId)521         void launchTestActivityOnDisplaySync(@Nullable String className, Intent intent,
522                 int displayId) {
523             launchTestActivityOnDisplaySync(className, intent, displayId, WINDOWING_MODE_UNDEFINED);
524         }
525 
526         /**
527          * Launches an {@link Activity} synchronously on a target display. The class name needs to
528          * be provided either implicitly through the {@link Intent} or explicitly as a parameter
529          *
530          * @param className Optional class name of expected activity
531          * @param intent Intent to launch an activity
532          * @param displayId ID for the target display
533          * @param windowingMode Windowing mode at launch
534          */
launchTestActivityOnDisplaySync( @ullable String className, Intent intent, int displayId, int windowingMode)535         void launchTestActivityOnDisplaySync(
536                 @Nullable String className, Intent intent, int displayId, int windowingMode) {
537             runWithShellPermission(
538                     () -> {
539                         mTestActivity =
540                                 launchActivityOnDisplay(
541                                         className, intent, displayId, windowingMode);
542                         // Check activity is launched and resumed.
543                         final ComponentName testActivityName = mTestActivity.getComponentName();
544                         waitAndAssertTopResumedActivity(
545                                 testActivityName, displayId, "Activity must be resumed");
546                     });
547         }
548 
549         /**
550          * Launches an {@link Activity} on a target display asynchronously.
551          * @param activityClass The {@link Activity} class to be launched
552          * @param displayId ID of the target display
553          */
launchTestActivityOnDisplay(Class<T> activityClass, int displayId)554         void launchTestActivityOnDisplay(Class<T> activityClass, int displayId) {
555             final Intent intent = new Intent(mContext, activityClass)
556                     .addFlags(FLAG_ACTIVITY_NEW_TASK);
557             final String className = intent.getComponent().getClassName();
558             runWithShellPermission(
559                     () -> {
560                         mTestActivity =
561                                 launchActivityOnDisplay(
562                                         className, intent, displayId, WINDOWING_MODE_UNDEFINED);
563                         assertNotNull(mTestActivity);
564                     });
565         }
566 
567         /**
568          * Launches an {@link Activity} on a target display. In order to return the correct activity
569          * the class name or an explicit {@link Intent} must be provided.
570          *
571          * @param className Optional class name of expected activity
572          * @param intent {@link Intent} to launch an activity
573          * @param displayId ID for the target display
574          * @param windowingMode Windowing mode at launch
575          * @return The {@link Activity} that was launched
576          */
launchActivityOnDisplay( @ullable String className, Intent intent, int displayId, int windowingMode)577         private T launchActivityOnDisplay(
578                 @Nullable String className, Intent intent, int displayId, int windowingMode) {
579             final String localClassName = className != null ? className :
580               (intent.getComponent() != null ? intent.getComponent().getClassName() : null);
581             if (localClassName == null || localClassName.isEmpty()) {
582                 fail("Must provide either a class name or an intent with a component");
583             }
584             final ActivityOptions launchOptions = ActivityOptions.makeBasic();
585             launchOptions.setLaunchDisplayId(displayId);
586             launchOptions.setLaunchWindowingMode(windowingMode);
587             final Bundle bundle = launchOptions.toBundle();
588             final ActivityMonitor monitor = mInstrumentation.addMonitor(localClassName, null,
589                     false);
590             mContext.startActivity(intent.addFlags(FLAG_ACTIVITY_NEW_TASK), bundle);
591             // Wait for activity launch with timeout.
592             mTestActivity = (T) mInstrumentation.waitForMonitorWithTimeout(monitor,
593                     ACTIVITY_LAUNCH_TIMEOUT);
594             assertNotNull(mTestActivity);
595             return mTestActivity;
596         }
597 
finishCurrentActivityNoWait()598         void finishCurrentActivityNoWait() {
599             if (mTestActivity != null) {
600                 mTestActivity.finishAndRemoveTask();
601                 mTestActivity = null;
602             }
603         }
604 
runOnMainSyncAndWait(Runnable runnable)605         void runOnMainSyncAndWait(Runnable runnable) {
606             mInstrumentation.runOnMainSync(runnable);
607             mInstrumentation.waitForIdleSync();
608         }
609 
runOnMainAndAssertWithTimeout(@onNull BooleanSupplier condition, long timeoutMs, String message)610         void runOnMainAndAssertWithTimeout(@NonNull BooleanSupplier condition, long timeoutMs,
611                 String message) {
612             final AtomicBoolean result = new AtomicBoolean();
613             final long expiredTime = System.currentTimeMillis() + timeoutMs;
614             while (!result.get()) {
615                 if (System.currentTimeMillis() >= expiredTime) {
616                     fail(message);
617                 }
618                 runOnMainSyncAndWait(() -> {
619                     if (condition.getAsBoolean()) {
620                         result.set(true);
621                     }
622                 });
623                 SystemClock.sleep(WAIT_SLICE);
624             }
625         }
626 
getActivity()627         public T getActivity() {
628             return mTestActivity;
629         }
630 
631         @Override
close()632         public void close() {
633             if (mTestActivity != null && mFinishAfterClose) {
634                 mTestActivity.finishAndRemoveTask();
635             }
636         }
637     }
638 
639     @Before
setUp()640     public void setUp() throws Exception {
641         if (isKeyguardLocked() || !Objects.requireNonNull(
642                 mContext.getSystemService(PowerManager.class)).isInteractive()) {
643             pressWakeupButton();
644             pressUnlockButton();
645         }
646         launchHomeActivityNoWait();
647         // TODO(b/242933292): Consider removing all the tasks belonging to android.server.wm
648         // instead of removing all and then waiting for allActivitiesResumed.
649         removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
650 
651         runWithShellPermission(() -> {
652             // TaskOrganizer ctor requires MANAGE_ACTIVITY_TASKS permission
653             mTaskOrganizer = new TestTaskOrganizer();
654             // Clear launch params for all test packages to make sure each test is run in a clean
655             // state.
656             mAtm.clearLaunchParamsForPackages(TEST_PACKAGES);
657         });
658 
659         // removeRootTaskWithActivityTypes() removes all the tasks apart from home. In a few cases,
660         // the systemUI might have a few tasks that need to be displayed all the time.
661         // For such tasks, systemUI might have a restart-logic that restarts those tasks. Those
662         // restarts can interfere with the test state. To avoid that, its better to wait for all
663         // the activities to come in the resumed state.
664         mWmState.waitForWithAmState(WindowManagerState::allActivitiesResumed, "Root Tasks should "
665                 + "be either empty or resumed");
666     }
667 
668     /** It always executes after {@link org.junit.After}. */
tearDownBase()669     private void tearDownBase() {
670         mObjectTracker.tearDown(mPostAssertionRule::addError);
671 
672         if (mTaskOrganizer != null) {
673             mTaskOrganizer.unregisterOrganizerIfNeeded();
674         }
675         // Synchronous execution of removeRootTasksWithActivityTypes() ensures that all
676         // activities but home are cleaned up from the root task at the end of each test. Am force
677         // stop shell commands might be asynchronous and could interrupt the task cleanup
678         // process if executed first.
679         launchHomeActivityNoWait();
680         removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
681         stopTestPackage(TEST_PACKAGE);
682         stopTestPackage(SECOND_TEST_PACKAGE);
683         stopTestPackage(THIRD_TEST_PACKAGE);
684         if (mShouldWaitForAllNonHomeActivitiesToDestroyed) {
685             mWmState.waitForAllNonHomeActivitiesToDestroyed();
686         }
687 
688         if (mWaitForRotationOnTearDown) {
689             mWmState.waitForDisplayUnfrozen();
690         }
691 
692         if (ENABLE_SHELL_TRANSITIONS) {
693             if (!mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY)) {
694                 fail("Shell Transition left unfinished!");
695             }
696         }
697     }
698 
699     /**
700      * After home key is pressed ({@link #pressHomeButton} is called), the later launch may be
701      * deferred if the calling uid doesn't have android.permission.STOP_APP_SWITCHES. This method
702      * will resume the temporary stopped state, so the launch won't be affected.
703      */
resumeAppSwitches()704     protected void resumeAppSwitches() {
705         SystemUtil.runWithShellPermissionIdentity(ActivityManager::resumeAppSwitches);
706     }
707 
startActivityOnDisplay(int displayId, ComponentName component)708     protected void startActivityOnDisplay(int displayId, ComponentName component) {
709         final ActivityOptions options = ActivityOptions.makeBasic();
710         options.setLaunchDisplayId(displayId);
711 
712         mContext.startActivity(new Intent().addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
713                 .setComponent(component), options.toBundle());
714     }
715 
noHomeScreen()716     protected boolean noHomeScreen() {
717         try {
718             return mContext.getResources().getBoolean(
719                     Resources.getSystem().getIdentifier("config_noHomeScreen", "bool",
720                             "android"));
721         } catch (Resources.NotFoundException e) {
722             // Assume there's a home screen.
723             return false;
724         }
725     }
726 
getSupportsSystemDecorsOnSecondaryDisplays()727     private boolean getSupportsSystemDecorsOnSecondaryDisplays() {
728         try {
729             return mContext.getResources().getBoolean(
730                     Resources.getSystem().getIdentifier(
731                             "config_supportsSystemDecorsOnSecondaryDisplays", "bool", "android"));
732         } catch (Resources.NotFoundException e) {
733             // Assume this device support system decorations.
734             return true;
735         }
736     }
737 
getDefaultSecondaryHomeComponent()738     protected ComponentName getDefaultSecondaryHomeComponent() {
739         assumeTrue(supportsMultiDisplay());
740         int resId = Resources.getSystem().getIdentifier(
741                 "config_secondaryHomePackage", "string", "android");
742         final Intent intent = new Intent(Intent.ACTION_MAIN);
743         intent.addCategory(Intent.CATEGORY_SECONDARY_HOME);
744         intent.setPackage(mContext.getResources().getString(resId));
745         final ResolveInfo resolveInfo =
746                 mContext.getPackageManager().resolveActivity(intent, MATCH_DEFAULT_ONLY);
747         assertNotNull("Should have default secondary home activity", resolveInfo);
748 
749         return new ComponentName(resolveInfo.activityInfo.packageName,
750                 resolveInfo.activityInfo.name);
751     }
752 
753     /**
754      * Insert an input event (ACTION_DOWN -> ACTION_CANCEL) to ensures the display to be focused
755      * without triggering potential clicked to impact the test environment.
756      * (e.g: Keyguard credential activated unexpectedly.)
757      *
758      * @param displayId the display ID to gain focused by inject swipe action
759      */
touchAndCancelOnDisplayCenterSync(int displayId)760     protected void touchAndCancelOnDisplayCenterSync(int displayId) {
761         mTouchHelper.touchAndCancelOnDisplayCenterSync(displayId);
762     }
763 
tapOnDisplaySync(int x, int y, int displayId)764     protected void tapOnDisplaySync(int x, int y, int displayId) {
765         mTouchHelper.tapOnDisplaySync(x, y, displayId);
766     }
767 
tapOnDisplay(int x, int y, int displayId, boolean sync)768     private void tapOnDisplay(int x, int y, int displayId, boolean sync) {
769         mTouchHelper.tapOnDisplay(x, y, displayId, sync);
770     }
771 
tapOnCenter(Rect bounds, int displayId)772     protected void tapOnCenter(Rect bounds, int displayId) {
773         mTouchHelper.tapOnCenter(bounds, displayId);
774     }
775 
tapOnViewCenter(View view)776     protected void tapOnViewCenter(View view) {
777         mTouchHelper.tapOnViewCenter(view);
778     }
779 
tapOnTaskCenter(Task task)780     protected void tapOnTaskCenter(Task task) {
781         mTouchHelper.tapOnTaskCenter(task);
782     }
783 
tapOnDisplayCenter(int displayId)784     protected void tapOnDisplayCenter(int displayId) {
785         mTouchHelper.tapOnDisplayCenter(displayId);
786     }
787 
tapOnDisplayCenterAsync(int displayId)788     protected void tapOnDisplayCenterAsync(int displayId) {
789         mTouchHelper.tapOnDisplayCenterAsync(displayId);
790     }
791 
injectKey(int keyCode, boolean longPress, boolean sync)792     public static void injectKey(int keyCode, boolean longPress, boolean sync) {
793         TouchHelper.injectKey(keyCode, longPress, sync);
794     }
795 
removeRootTasksWithActivityTypes(int... activityTypes)796     protected void removeRootTasksWithActivityTypes(int... activityTypes) {
797         runWithShellPermission(() -> mAtm.removeRootTasksWithActivityTypes(activityTypes));
798         waitForIdle();
799     }
800 
removeRootTasksInWindowingModes(int... windowingModes)801     protected void removeRootTasksInWindowingModes(int... windowingModes) {
802         runWithShellPermission(() -> mAtm.removeRootTasksInWindowingModes(windowingModes));
803         waitForIdle();
804     }
805 
removeRootTask(int taskId)806     protected void removeRootTask(int taskId) {
807         runWithShellPermission(() -> mAtm.removeTask(taskId));
808         waitForIdle();
809     }
810 
executeShellCommand(String command)811     public static String executeShellCommand(String command) {
812         log("Shell command: " + command);
813         try {
814             return SystemUtil.runShellCommand(getInstrumentation(), command);
815         } catch (IOException e) {
816             //bubble it up
817             logE("Error running shell command: " + command);
818             throw new RuntimeException(e);
819         }
820     }
821 
takeScreenshot()822     protected Bitmap takeScreenshot() {
823         return mInstrumentation.getUiAutomation().takeScreenshot();
824     }
825 
takeScreenshotForBounds(Rect rect)826     protected Bitmap takeScreenshotForBounds(Rect rect) {
827         Bitmap fullBitmap = takeScreenshot();
828         return Bitmap.createBitmap(fullBitmap, rect.left, rect.top,
829                 rect.width(), rect.height());
830     }
831 
launchActivity(final ComponentName activityName, final CliIntentExtra... extras)832     protected void launchActivity(final ComponentName activityName,
833             final CliIntentExtra... extras) {
834         launchActivityNoWait(activityName, extras);
835         mWmState.waitForValidState(activityName);
836     }
837 
launchActivityNoWait(final ComponentName activityName, final CliIntentExtra... extras)838     protected void launchActivityNoWait(final ComponentName activityName,
839             final CliIntentExtra... extras) {
840         executeShellCommand(getAmStartCmd(activityName, extras));
841     }
842 
launchActivityInNewTask(final ComponentName activityName)843     protected void launchActivityInNewTask(final ComponentName activityName) {
844         executeShellCommand(getAmStartCmdInNewTask(activityName));
845         mWmState.waitForValidState(activityName);
846     }
847 
launchActivityWithData(final ComponentName activityName, String data)848     protected void launchActivityWithData(final ComponentName activityName, String data) {
849         executeShellCommand(getAmStartCmdWithData(activityName, data));
850         mWmState.waitForValidState(activityName);
851     }
852 
launchActivityWithDismissKeyguard(final ComponentName activityName)853     protected void launchActivityWithDismissKeyguard(final ComponentName activityName) {
854         executeShellCommand(getAmStartCmdWithDismissKeyguard(activityName));
855         mWmState.waitForValidState(activityName);
856     }
857 
launchActivityWithNoUserAction(final ComponentName activityName, final CliIntentExtra... extras)858     protected void launchActivityWithNoUserAction(final ComponentName activityName,
859             final CliIntentExtra... extras) {
860         executeShellCommand(getAmStartCmdWithNoUserAction(activityName, extras));
861         mWmState.waitForValidState(activityName);
862     }
863 
launchActivityInFullscreen(final ComponentName activityName)864     protected void launchActivityInFullscreen(final ComponentName activityName) {
865         executeShellCommand(
866                 getAmStartCmdWithWindowingMode(activityName, WINDOWING_MODE_FULLSCREEN));
867         mWmState.waitForValidState(activityName);
868     }
869 
waitForIdle()870     protected static void waitForIdle() {
871         getInstrumentation().waitForIdleSync();
872     }
873 
waitForOrFail(String message, BooleanSupplier condition)874     static void waitForOrFail(String message, BooleanSupplier condition) {
875         Condition.waitFor(new Condition<>(message, condition)
876                 .setRetryIntervalMs(500)
877                 .setRetryLimit(20)
878                 .setOnFailure(unusedResult -> fail("FAILED because unsatisfied: " + message)));
879     }
880 
881     /** Returns the root task that contains the provided leaf task id. */
getRootTaskForLeafTaskId(int taskId)882     protected Task getRootTaskForLeafTaskId(int taskId) {
883         mWmState.computeState();
884         final List<Task> rootTasks = mWmState.getRootTasks();
885         for (Task rootTask : rootTasks) {
886             if (rootTask.getTask(taskId) != null) {
887                 return rootTask;
888             }
889         }
890         return null;
891     }
892 
getRootTask(int taskId)893     protected Task getRootTask(int taskId) {
894         mWmState.computeState();
895         final List<Task> rootTasks = mWmState.getRootTasks();
896         for (Task rootTask : rootTasks) {
897             if (rootTask.getTaskId() == taskId) {
898                 return rootTask;
899             }
900         }
901         return null;
902     }
903 
getDisplayWindowingModeByActivity(ComponentName activity)904     protected int getDisplayWindowingModeByActivity(ComponentName activity) {
905         return mWmState.getDisplay(mWmState.getDisplayByActivity(activity)).getWindowingMode();
906     }
907 
908     /**
909      * Launches the home activity directly. If there is no specific reason to simulate a home key
910      * (which will trigger stop-app-switches), it is the recommended method to go home.
911      */
launchHomeActivityNoWait()912     protected static void launchHomeActivityNoWait() {
913         // dismiss all system dialogs before launch home.
914         executeShellCommand(AM_BROADCAST_CLOSE_SYSTEM_DIALOGS);
915         executeShellCommand(AM_START_HOME_ACTIVITY_COMMAND);
916     }
917 
918     /** Launches the home activity directly with waiting for it to be visible. */
launchHomeActivity()919     protected void launchHomeActivity() {
920         launchHomeActivityNoWait();
921         mWmState.waitForHomeActivityVisible();
922     }
923 
launchActivityNoWait(ComponentName activityName, int windowingMode, final CliIntentExtra... extras)924     protected void launchActivityNoWait(ComponentName activityName, int windowingMode,
925             final CliIntentExtra... extras) {
926         executeShellCommand(getAmStartCmd(activityName, extras)
927                 + " --windowingMode " + windowingMode);
928     }
929 
launchActivity(ComponentName activityName, int windowingMode, final CliIntentExtra... keyValuePairs)930     protected void launchActivity(ComponentName activityName, int windowingMode,
931             final CliIntentExtra... keyValuePairs) {
932         launchActivityNoWait(activityName, windowingMode, keyValuePairs);
933         mWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
934                 .setWindowingMode(windowingMode)
935                 .build());
936     }
937 
launchActivityOnDisplay(ComponentName activityName, int windowingMode, int displayId, final CliIntentExtra... extras)938     protected void launchActivityOnDisplay(ComponentName activityName, int windowingMode,
939             int displayId, final CliIntentExtra... extras) {
940         executeShellCommand(getAmStartCmd(activityName, displayId, extras)
941                 + " --windowingMode " + windowingMode);
942         mWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
943                 .setWindowingMode(windowingMode)
944                 .build());
945     }
946 
launchActivityOnDisplay(ComponentName activityName, int displayId, CliIntentExtra... extras)947     protected void launchActivityOnDisplay(ComponentName activityName, int displayId,
948             CliIntentExtra... extras) {
949         launchActivityOnDisplayNoWait(activityName, displayId, extras);
950         mWmState.waitForValidState(activityName);
951     }
952 
launchActivityOnDisplayNoWait(ComponentName activityName, int displayId, CliIntentExtra... extras)953     protected void launchActivityOnDisplayNoWait(ComponentName activityName, int displayId,
954             CliIntentExtra... extras) {
955         executeShellCommand(getAmStartCmd(activityName, displayId, extras));
956     }
957 
launchActivityInPrimarySplit(ComponentName activityName)958     protected void launchActivityInPrimarySplit(ComponentName activityName) {
959         runWithShellPermission(() -> {
960             launchActivity(activityName);
961             final int taskId = mWmState.getTaskByActivity(activityName).mTaskId;
962             mTaskOrganizer.putTaskInSplitPrimary(taskId);
963             mWmState.waitForValidState(activityName);
964         });
965     }
966 
launchActivityInSecondarySplit(ComponentName activityName)967     protected void launchActivityInSecondarySplit(ComponentName activityName) {
968         runWithShellPermission(() -> {
969             launchActivity(activityName);
970             final int taskId = mWmState.getTaskByActivity(activityName).mTaskId;
971             mTaskOrganizer.putTaskInSplitSecondary(taskId);
972             mWmState.waitForValidState(activityName);
973         });
974     }
975 
putActivityInPrimarySplit(ComponentName activityName)976     protected void putActivityInPrimarySplit(ComponentName activityName) {
977         final int taskId = mWmState.getTaskByActivity(activityName).mTaskId;
978         mTaskOrganizer.putTaskInSplitPrimary(taskId);
979         mWmState.waitForValidState(activityName);
980     }
981 
putActivityInSecondarySplit(ComponentName activityName)982     protected void putActivityInSecondarySplit(ComponentName activityName) {
983         final int taskId = mWmState.getTaskByActivity(activityName).mTaskId;
984         mTaskOrganizer.putTaskInSplitSecondary(taskId);
985         mWmState.waitForValidState(activityName);
986     }
987 
988     /**
989      * Launches {@param primaryActivity} into split-screen primary windowing mode
990      * and {@param secondaryActivity} to the side in split-screen secondary windowing mode.
991      */
launchActivitiesInSplitScreen(LaunchActivityBuilder primaryActivity, LaunchActivityBuilder secondaryActivity)992     protected void launchActivitiesInSplitScreen(LaunchActivityBuilder primaryActivity,
993             LaunchActivityBuilder secondaryActivity) {
994         // Launch split-screen primary.
995         primaryActivity
996                 .setUseInstrumentation()
997                 .setWaitForLaunched(true)
998                 .execute();
999 
1000         final int primaryTaskId = mWmState.getTaskByActivity(
1001                 primaryActivity.mTargetActivity).mTaskId;
1002         mTaskOrganizer.putTaskInSplitPrimary(primaryTaskId);
1003 
1004         // Launch split-screen secondary
1005         secondaryActivity
1006                 .setUseInstrumentation()
1007                 .setWaitForLaunched(true)
1008                 .setNewTask(true)
1009                 .setMultipleTask(true)
1010                 .execute();
1011 
1012         final int secondaryTaskId = mWmState.getTaskByActivity(
1013                 secondaryActivity.mTargetActivity).mTaskId;
1014         mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId);
1015         mWmState.computeState(primaryActivity.getTargetActivity(),
1016                 secondaryActivity.getTargetActivity());
1017         log("launchActivitiesInSplitScreen(), primaryTaskId=" + primaryTaskId +
1018                 ", secondaryTaskId=" + secondaryTaskId);
1019     }
1020 
1021     /**
1022      * Move the task of {@param primaryActivity} into split-screen primary and the task of
1023      * {@param secondaryActivity} to the side in split-screen secondary.
1024      */
moveActivitiesToSplitScreen(ComponentName primaryActivity, ComponentName secondaryActivity)1025     protected void moveActivitiesToSplitScreen(ComponentName primaryActivity,
1026             ComponentName secondaryActivity) {
1027         final int primaryTaskId = mWmState.getTaskByActivity(primaryActivity).mTaskId;
1028         mTaskOrganizer.putTaskInSplitPrimary(primaryTaskId);
1029 
1030         final int secondaryTaskId = mWmState.getTaskByActivity(secondaryActivity).mTaskId;
1031         mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId);
1032 
1033         mWmState.computeState(primaryActivity, secondaryActivity);
1034         log("moveActivitiesToSplitScreen(), primaryTaskId=" + primaryTaskId +
1035                 ", secondaryTaskId=" + secondaryTaskId);
1036     }
1037 
dismissSplitScreen(boolean primaryOnTop)1038     protected void dismissSplitScreen(boolean primaryOnTop) {
1039         if (mTaskOrganizer != null) {
1040             mTaskOrganizer.dismissSplitScreen(primaryOnTop);
1041         }
1042     }
1043 
1044     /**
1045      * Move activity to root task or on top of the given root task when the root task is also a leaf
1046      * task.
1047      */
moveActivityToRootTaskOrOnTop(ComponentName activityName, int rootTaskId)1048     protected void moveActivityToRootTaskOrOnTop(ComponentName activityName, int rootTaskId) {
1049         mWmState.computeState(activityName);
1050         Task rootTask = getRootTask(rootTaskId);
1051         if (rootTask.getActivities().size() != 0) {
1052             // If the root task is a 1-level task, start the activity on top of given task.
1053             getLaunchActivityBuilder()
1054                     .setDisplayId(rootTask.mDisplayId)
1055                     .setWindowingMode(rootTask.getWindowingMode())
1056                     .setActivityType(rootTask.getActivityType())
1057                     .setTargetActivity(activityName)
1058                     .allowMultipleInstances(false)
1059                     .setUseInstrumentation()
1060                     .execute();
1061         } else {
1062             final int taskId = mWmState.getTaskByActivity(activityName).mTaskId;
1063             runWithShellPermission(() -> mAtm.moveTaskToRootTask(taskId, rootTaskId, true));
1064         }
1065         mWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
1066                 .setRootTaskId(rootTaskId)
1067                 .build());
1068     }
1069 
resizeActivityTask( ComponentName activityName, int left, int top, int right, int bottom)1070     protected void resizeActivityTask(
1071             ComponentName activityName, int left, int top, int right, int bottom) {
1072         mWmState.computeState(activityName);
1073         final int taskId = mWmState.getTaskByActivity(activityName).mTaskId;
1074         runWithShellPermission(() -> mAtm.resizeTask(taskId, new Rect(left, top, right, bottom)));
1075     }
1076 
supportsVrMode()1077     protected boolean supportsVrMode() {
1078         return hasDeviceFeature(FEATURE_VR_MODE_HIGH_PERFORMANCE);
1079     }
1080 
supportsPip()1081     protected boolean supportsPip() {
1082         return hasDeviceFeature(FEATURE_PICTURE_IN_PICTURE)
1083                 || PRETEND_DEVICE_SUPPORTS_PIP;
1084     }
1085 
supportsExpandedPip()1086     protected boolean supportsExpandedPip() {
1087         return hasDeviceFeature(FEATURE_EXPANDED_PICTURE_IN_PICTURE);
1088     }
1089 
supportsFreeform()1090     protected boolean supportsFreeform() {
1091         return hasDeviceFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
1092                 || PRETEND_DEVICE_SUPPORTS_FREEFORM;
1093     }
1094 
1095     /** Whether or not the device supports lock screen. */
supportsLockScreen()1096     protected boolean supportsLockScreen() {
1097         return supportsInsecureLock() || supportsSecureLock();
1098     }
1099 
1100     /** Whether or not the device supports pin/pattern/password lock. */
supportsSecureLock()1101     protected boolean supportsSecureLock() {
1102         return hasDeviceFeature(FEATURE_SECURE_LOCK_SCREEN);
1103     }
1104 
1105     /** Whether or not the device supports "swipe" lock. */
supportsInsecureLock()1106     protected boolean supportsInsecureLock() {
1107         return !hasDeviceFeature(FEATURE_LEANBACK)
1108                 && !hasDeviceFeature(FEATURE_WATCH)
1109                 && !hasDeviceFeature(FEATURE_EMBEDDED)
1110                 && !hasDeviceFeature(FEATURE_AUTOMOTIVE)
1111                 && getSupportsInsecureLockScreen();
1112     }
1113 
supportsBlur()1114     protected boolean supportsBlur() {
1115         return SystemProperties.get("ro.surface_flinger.supports_background_blur", "default")
1116                 .equals("1");
1117     }
1118 
isWatch()1119     protected boolean isWatch() {
1120         return hasDeviceFeature(FEATURE_WATCH);
1121     }
1122 
isCar()1123     protected boolean isCar() {
1124         return hasDeviceFeature(FEATURE_AUTOMOTIVE);
1125     }
1126 
isLeanBack()1127     protected boolean isLeanBack() {
1128         return hasDeviceFeature(FEATURE_TELEVISION);
1129     }
1130 
isTablet()1131     public static boolean isTablet() {
1132         if (sIsTablet == null) {
1133             // Use WindowContext with type application overlay to prevent the metrics overridden by
1134             // activity bounds. Note that process configuration may still be overridden by
1135             // foreground Activity.
1136             final Context appContext = ApplicationProvider.getApplicationContext();
1137             final Display defaultDisplay = appContext.getSystemService(DisplayManager.class)
1138                     .getDisplay(DEFAULT_DISPLAY);
1139             final Context windowContext = appContext.createWindowContext(defaultDisplay,
1140                     TYPE_APPLICATION_OVERLAY, null /* options */);
1141             sIsTablet = windowContext.getResources()
1142                     .getConfiguration().smallestScreenWidthDp >= 600;
1143         }
1144         return sIsTablet;
1145     }
1146 
waitAndAssertActivityState(ComponentName activityName, String state, String message)1147     protected void waitAndAssertActivityState(ComponentName activityName,
1148             String state, String message) {
1149         mWmState.waitForActivityState(activityName, state);
1150 
1151         assertTrue(message, mWmState.hasActivityState(activityName, state));
1152     }
1153 
isKeyguardLocked()1154     protected boolean isKeyguardLocked() {
1155         return mKm != null && mKm.isKeyguardLocked();
1156     }
1157 
waitAndAssertActivityStateOnDisplay(ComponentName activityName, String state, int displayId, String message)1158     protected void waitAndAssertActivityStateOnDisplay(ComponentName activityName, String state,
1159             int displayId, String message) {
1160         waitAndAssertActivityState(activityName, state, message);
1161         assertEquals(message, mWmState.getDisplayByActivity(activityName),
1162                 displayId);
1163     }
1164 
waitAndAssertTopResumedActivity(ComponentName activityName, int displayId, String message)1165     public void waitAndAssertTopResumedActivity(ComponentName activityName, int displayId,
1166             String message) {
1167         final String activityClassName = getActivityName(activityName);
1168         mWmState.waitForWithAmState(state -> activityClassName.equals(state.getFocusedActivity()),
1169                 "activity to be on top");
1170         waitAndAssertResumedActivity(activityName, "Activity must be resumed");
1171         mWmState.assertFocusedActivity(message, activityName);
1172 
1173         final int frontRootTaskId = mWmState.getFrontRootTaskId(displayId);
1174         Task frontRootTaskOnDisplay = mWmState.getRootTask(frontRootTaskId);
1175         assertEquals(
1176                 "Resumed activity of front root task of the target display must match. " + message,
1177                 activityClassName,
1178                 frontRootTaskOnDisplay.isLeafTask() ? frontRootTaskOnDisplay.mResumedActivity
1179                         : frontRootTaskOnDisplay.getTopTask().mResumedActivity);
1180         mWmState.assertFocusedRootTask("Top activity's rootTask must also be on top",
1181                 frontRootTaskId);
1182     }
1183 
1184     /**
1185      * Waits and asserts that the activity represented by the given activity name is resumed and
1186      * visible, but is not necessarily the top activity.
1187      *
1188      * @param activityName the activity name
1189      * @param message the error message
1190      */
waitAndAssertResumedActivity(ComponentName activityName, String message)1191     public void waitAndAssertResumedActivity(ComponentName activityName, String message) {
1192         mWmState.waitForValidState(activityName);
1193         mWmState.waitForActivityState(activityName, STATE_RESUMED);
1194         mWmState.assertValidity();
1195         assertTrue(message, mWmState.hasActivityState(activityName, STATE_RESUMED));
1196         mWmState.assertVisibility(activityName, true /* visible */);
1197     }
1198 
1199     /**
1200      * Waits and asserts that the activity represented by the given activity name is stopped and
1201      * invisible.
1202      *
1203      * @param activityName the activity name
1204      * @param message the error message
1205      */
waitAndAssertStoppedActivity(ComponentName activityName, String message)1206     public void waitAndAssertStoppedActivity(ComponentName activityName, String message) {
1207         mWmState.waitForValidState(activityName);
1208         mWmState.waitForActivityState(activityName, STATE_STOPPED);
1209         mWmState.assertValidity();
1210         assertTrue(message, mWmState.hasActivityState(activityName, STATE_STOPPED));
1211         mWmState.assertVisibility(activityName, false /* visible */);
1212     }
1213 
1214     // TODO: Switch to using a feature flag, when available.
isUiModeLockedToVrHeadset()1215     protected static boolean isUiModeLockedToVrHeadset() {
1216         final String output = runCommandAndPrintOutput("dumpsys uimode");
1217 
1218         Integer curUiMode = null;
1219         Boolean uiModeLocked = null;
1220         for (String line : output.split("\\n")) {
1221             line = line.trim();
1222             Matcher matcher = sCurrentUiModePattern.matcher(line);
1223             if (matcher.find()) {
1224                 curUiMode = Integer.parseInt(matcher.group(1), 16);
1225             }
1226             matcher = sUiModeLockedPattern.matcher(line);
1227             if (matcher.find()) {
1228                 uiModeLocked = matcher.group(1).equals("true");
1229             }
1230         }
1231 
1232         boolean uiModeLockedToVrHeadset = (curUiMode != null) && (uiModeLocked != null)
1233                 && ((curUiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET) && uiModeLocked;
1234 
1235         if (uiModeLockedToVrHeadset) {
1236             log("UI mode is locked to VR headset");
1237         }
1238 
1239         return uiModeLockedToVrHeadset;
1240     }
1241 
supportsMultiWindow()1242     protected boolean supportsMultiWindow() {
1243         Display defaultDisplay = mDm.getDisplay(DEFAULT_DISPLAY);
1244         return ActivityTaskManager.supportsSplitScreenMultiWindow(
1245                 mContext.createDisplayContext(defaultDisplay));
1246     }
1247 
1248     /** Returns true if the default display supports split screen multi-window. */
supportsSplitScreenMultiWindow()1249     protected boolean supportsSplitScreenMultiWindow() {
1250         Display defaultDisplay = mDm.getDisplay(DEFAULT_DISPLAY);
1251         return supportsSplitScreenMultiWindow(mContext.createDisplayContext(defaultDisplay));
1252     }
1253 
1254     /**
1255      * Returns true if the display associated with the supplied {@code context} supports split
1256      * screen multi-window.
1257      */
supportsSplitScreenMultiWindow(Context context)1258     protected boolean supportsSplitScreenMultiWindow(Context context) {
1259         return ActivityTaskManager.supportsSplitScreenMultiWindow(context);
1260     }
1261 
hasHomeScreen()1262     protected boolean hasHomeScreen() {
1263         if (sHasHomeScreen == null) {
1264             sHasHomeScreen = !noHomeScreen();
1265         }
1266         return sHasHomeScreen;
1267     }
1268 
supportsSystemDecorsOnSecondaryDisplays()1269     protected boolean supportsSystemDecorsOnSecondaryDisplays() {
1270         if (sSupportsSystemDecorsOnSecondaryDisplays == null) {
1271             sSupportsSystemDecorsOnSecondaryDisplays = getSupportsSystemDecorsOnSecondaryDisplays();
1272         }
1273         return sSupportsSystemDecorsOnSecondaryDisplays;
1274     }
1275 
getSupportsInsecureLockScreen()1276     protected boolean getSupportsInsecureLockScreen() {
1277         if (sSupportsInsecureLockScreen == null) {
1278             try {
1279                 sSupportsInsecureLockScreen = mContext.getResources().getBoolean(
1280                         Resources.getSystem().getIdentifier(
1281                                 "config_supportsInsecureLockScreen", "bool", "android"));
1282             } catch (Resources.NotFoundException e) {
1283                 sSupportsInsecureLockScreen = true;
1284             }
1285         }
1286         return sSupportsInsecureLockScreen;
1287     }
1288 
isAssistantOnTopOfDream()1289     protected boolean isAssistantOnTopOfDream() {
1290         if (sIsAssistantOnTop == null) {
1291             sIsAssistantOnTop = mContext.getResources().getBoolean(
1292                     android.R.bool.config_assistantOnTopOfDream);
1293         }
1294         return sIsAssistantOnTop;
1295     }
1296 
dismissDreamOnActivityStart()1297     protected boolean dismissDreamOnActivityStart() {
1298         if (sDismissDreamOnActivityStart == null) {
1299             try {
1300                 sDismissDreamOnActivityStart = mContext.getResources().getBoolean(
1301                         Resources.getSystem().getIdentifier(
1302                                 "config_dismissDreamOnActivityStart", "bool", "android"));
1303             } catch (Resources.NotFoundException e) {
1304                 sDismissDreamOnActivityStart = true;
1305             }
1306         }
1307         return sDismissDreamOnActivityStart;
1308     }
1309 
1310     /**
1311      * Rotation support is indicated by explicitly having both landscape and portrait
1312      * features or not listing either at all.
1313      */
supportsRotation()1314     protected boolean supportsRotation() {
1315         final boolean supportsLandscape = hasDeviceFeature(FEATURE_SCREEN_LANDSCAPE);
1316         final boolean supportsPortrait = hasDeviceFeature(FEATURE_SCREEN_PORTRAIT);
1317         return (supportsLandscape && supportsPortrait)
1318                 || (!supportsLandscape && !supportsPortrait);
1319     }
1320 
1321     /**
1322      * The device should support orientation request from apps if it supports rotation and the
1323      * display is not close to square.
1324      */
supportsOrientationRequest()1325     protected boolean supportsOrientationRequest() {
1326         return supportsRotation() && !isCloseToSquareDisplay();
1327     }
1328 
1329     /** Checks whether the display dimension is close to square. */
isCloseToSquareDisplay()1330     protected boolean isCloseToSquareDisplay() {
1331         final Resources resources = mContext.getResources();
1332         final float closeToSquareMaxAspectRatio;
1333         try {
1334             closeToSquareMaxAspectRatio = resources.getFloat(resources.getIdentifier(
1335                     "config_closeToSquareDisplayMaxAspectRatio", "dimen", "android"));
1336         } catch (Resources.NotFoundException e) {
1337             // Assume device is not close to square.
1338             return false;
1339         }
1340         final DisplayMetrics displayMetrics = new DisplayMetrics();
1341         mDm.getDisplay(DEFAULT_DISPLAY).getRealMetrics(displayMetrics);
1342         final int w = displayMetrics.widthPixels;
1343         final int h = displayMetrics.heightPixels;
1344         final float aspectRatio = Math.max(w, h) / (float) Math.min(w, h);
1345         return aspectRatio <= closeToSquareMaxAspectRatio;
1346     }
1347 
hasDeviceFeature(final String requiredFeature)1348     protected boolean hasDeviceFeature(final String requiredFeature) {
1349         return mContext.getPackageManager()
1350                 .hasSystemFeature(requiredFeature);
1351     }
1352 
isDisplayPortrait()1353     protected static boolean isDisplayPortrait() {
1354         final DisplayManager displayManager = getInstrumentation()
1355                 .getContext().getSystemService(DisplayManager.class);
1356         final Display display = displayManager.getDisplay(DEFAULT_DISPLAY);
1357         final DisplayMetrics displayMetrics = new DisplayMetrics();
1358         display.getRealMetrics(displayMetrics);
1359         return displayMetrics.widthPixels < displayMetrics.heightPixels;
1360     }
1361 
isDisplayOn(int displayId)1362     protected static boolean isDisplayOn(int displayId) {
1363         final DisplayManager displayManager = getInstrumentation()
1364                 .getContext().getSystemService(DisplayManager.class);
1365         final Display display = displayManager.getDisplay(displayId);
1366         return display != null && display.getState() == Display.STATE_ON;
1367     }
1368 
perDisplayFocusEnabled()1369     protected static boolean perDisplayFocusEnabled() {
1370         return getInstrumentation().getTargetContext().getResources()
1371                 .getBoolean(android.R.bool.config_perDisplayFocusEnabled);
1372     }
1373 
removeLockCredential()1374     protected static void removeLockCredential() {
1375         runCommandAndPrintOutput("locksettings clear --old " + LOCK_CREDENTIAL);
1376     }
1377 
remoteInsetsControllerControlsSystemBars()1378     protected static boolean remoteInsetsControllerControlsSystemBars() {
1379         return getInstrumentation().getTargetContext().getResources()
1380                 .getBoolean(android.R.bool.config_remoteInsetsControllerControlsSystemBars);
1381     }
1382 
1383     /** @see ObjectTracker#manage(AutoCloseable) */
createManagedHomeActivitySession(ComponentName homeActivity)1384     protected HomeActivitySession createManagedHomeActivitySession(ComponentName homeActivity) {
1385         return mObjectTracker.manage(new HomeActivitySession(homeActivity));
1386     }
1387 
1388     /** @see ObjectTracker#manage(AutoCloseable) */
createManagedActivityClientSession()1389     protected ActivitySessionClient createManagedActivityClientSession() {
1390         return mObjectTracker.manage(new ActivitySessionClient(mContext));
1391     }
1392 
1393     /** @see ObjectTracker#manage(AutoCloseable) */
createManagedLockScreenSession()1394     protected LockScreenSession createManagedLockScreenSession() {
1395         return mObjectTracker.manage(new LockScreenSession());
1396     }
1397 
1398     /** @see ObjectTracker#manage(AutoCloseable) */
createManagedRotationSession()1399     protected RotationSession createManagedRotationSession() {
1400         return mObjectTracker.manage(new RotationSession());
1401     }
1402 
1403     /** @see ObjectTracker#manage(AutoCloseable) */
createManagedAodSession()1404     protected AodSession createManagedAodSession() {
1405         return mObjectTracker.manage(new AodSession());
1406     }
1407 
1408     /** @see ObjectTracker#manage(AutoCloseable) */
1409     protected DevEnableNonResizableMultiWindowSession
createManagedDevEnableNonResizableMultiWindowSession()1410     createManagedDevEnableNonResizableMultiWindowSession() {
1411         return mObjectTracker.manage(new DevEnableNonResizableMultiWindowSession());
1412     }
1413 
1414     /** @see ObjectTracker#manage(AutoCloseable) */
createManagedTestActivitySession()1415     protected <T extends Activity> TestActivitySession<T> createManagedTestActivitySession() {
1416         return new TestActivitySession<T>();
1417     }
1418 
1419     /** @see ObjectTracker#manage(AutoCloseable) */
createAllowSystemAlertWindowAppOpSession()1420     protected SystemAlertWindowAppOpSession createAllowSystemAlertWindowAppOpSession() {
1421         return mObjectTracker.manage(
1422                 new SystemAlertWindowAppOpSession(mContext.getOpPackageName(), MODE_ALLOWED));
1423     }
1424 
1425     /** @see ObjectTracker#manage(AutoCloseable) */
createManagedFontScaleSession()1426     protected FontScaleSession createManagedFontScaleSession() {
1427         return mObjectTracker.manage(new FontScaleSession());
1428     }
1429 
1430     /** Allows requesting orientation in case ignore_orientation_request is set to true. */
disableIgnoreOrientationRequest()1431     protected void disableIgnoreOrientationRequest() {
1432         mObjectTracker.manage(new IgnoreOrientationRequestSession(false /* enable */));
1433     }
1434 
1435     /**
1436      * Test @Rule class that disables screen doze settings before each test method running and
1437      * restoring to initial values after test method finished.
1438      */
1439     protected class DisableScreenDozeRule implements TestRule {
1440         AmbientDisplayConfiguration mConfig;
1441 
DisableScreenDozeRule()1442         DisableScreenDozeRule() {
1443             mConfig = new AmbientDisplayConfiguration(mContext);
1444         }
1445 
1446         @Override
apply(final Statement base, final Description description)1447         public Statement apply(final Statement base, final Description description) {
1448             return new Statement() {
1449                 @Override
1450                 public void evaluate() throws Throwable {
1451                     try {
1452                         SystemUtil.runWithShellPermissionIdentity(() -> {
1453                             // disable current doze settings
1454                             mConfig.disableDozeSettings(true /* shouldDisableNonUserConfigurable */,
1455                                     USER_SYSTEM);
1456                         });
1457                         base.evaluate();
1458                     } finally {
1459                         SystemUtil.runWithShellPermissionIdentity(() -> {
1460                             // restore doze settings
1461                             mConfig.restoreDozeSettings(USER_SYSTEM);
1462                         });
1463                     }
1464                 }
1465             };
1466         }
1467     }
1468 
1469     ComponentName getDefaultHomeComponent() {
1470         final Intent intent = new Intent(ACTION_MAIN);
1471         intent.addCategory(CATEGORY_HOME);
1472         intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
1473         final ResolveInfo resolveInfo =
1474                 mContext.getPackageManager().resolveActivity(intent, MATCH_DEFAULT_ONLY);
1475         if (resolveInfo == null) {
1476             throw new AssertionError("Home activity not found");
1477         }
1478         return new ComponentName(resolveInfo.activityInfo.packageName,
1479                 resolveInfo.activityInfo.name);
1480     }
1481 
1482     /**
1483      * HomeActivitySession is used to replace the default home component, so that you can use
1484      * your preferred home for testing within the session. The original default home will be
1485      * restored automatically afterward.
1486      */
1487     protected class HomeActivitySession implements AutoCloseable {
1488         private PackageManager mPackageManager;
1489         private ComponentName mOrigHome;
1490         private ComponentName mSessionHome;
1491 
1492         HomeActivitySession(ComponentName sessionHome) {
1493             mSessionHome = sessionHome;
1494             mPackageManager = mContext.getPackageManager();
1495             mOrigHome = getDefaultHomeComponent();
1496 
1497             runWithShellPermission(
1498                     () -> mPackageManager.setComponentEnabledSetting(mSessionHome,
1499                             COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP));
1500             setDefaultHome(mSessionHome);
1501         }
1502 
1503         @Override
1504         public void close() {
1505             runWithShellPermission(
1506                     () -> mPackageManager.setComponentEnabledSetting(mSessionHome,
1507                             COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP));
1508             if (mOrigHome != null) {
1509                 setDefaultHome(mOrigHome);
1510             }
1511         }
1512 
1513         private void setDefaultHome(ComponentName componentName) {
1514             executeShellCommand("cmd package set-home-activity --user "
1515                     + android.os.Process.myUserHandle().getIdentifier() + " "
1516                     + componentName.flattenToString());
1517         }
1518     }
1519 
1520     public class LockScreenSession implements AutoCloseable {
1521         private static final boolean DEBUG = false;
1522 
1523         private final boolean mIsLockDisabled;
1524         private boolean mLockCredentialSet;
1525         private boolean mRemoveActivitiesOnClose;
1526         private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
1527 
1528         public static final int FLAG_REMOVE_ACTIVITIES_ON_CLOSE = 1;
1529 
1530         public LockScreenSession() {
1531             this(0 /* flags */);
1532         }
1533 
1534         public LockScreenSession(int flags) {
1535             mIsLockDisabled = isLockDisabled();
1536             // Enable lock screen (swipe) by default.
1537             setLockDisabled(false);
1538             if ((flags & FLAG_REMOVE_ACTIVITIES_ON_CLOSE) != 0) {
1539                 mRemoveActivitiesOnClose = true;
1540             }
1541             mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
1542         }
1543 
1544         public LockScreenSession setLockCredential() {
1545             mLockCredentialSet = true;
1546             runCommandAndPrintOutput("locksettings set-pin " + LOCK_CREDENTIAL);
1547             return this;
1548         }
1549 
1550         public LockScreenSession enterAndConfirmLockCredential() {
1551             // Ensure focus will switch to default display. Meanwhile we cannot tap on center area,
1552             // which may tap on input credential area.
1553             touchAndCancelOnDisplayCenterSync(DEFAULT_DISPLAY);
1554 
1555             waitForDeviceIdle(3000);
1556             SystemUtil.runWithShellPermissionIdentity(() ->
1557                     mInstrumentation.sendStringSync(LOCK_CREDENTIAL));
1558             pressEnterButton();
1559             return this;
1560         }
1561 
1562         LockScreenSession disableLockScreen() {
1563             setLockDisabled(true);
1564             return this;
1565         }
1566 
1567         public LockScreenSession sleepDevice() {
1568             pressSleepButton();
1569             // Not all device variants lock when we go to sleep, so we need to explicitly lock the
1570             // device. Note that pressSleepButton() above is redundant because the action also
1571             // puts the device to sleep, but kept around for clarity.
1572             if (isWatch()) {
1573                 mInstrumentation.getUiAutomation().performGlobalAction(
1574                         AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN);
1575             }
1576             if (mAmbientDisplayConfiguration.alwaysOnEnabled(
1577                     android.os.Process.myUserHandle().getIdentifier())) {
1578                 mWmState.waitForAodShowing();
1579             } else {
1580                 Condition.waitFor("display to turn off", () -> !isDisplayOn(DEFAULT_DISPLAY));
1581             }
1582             if(!isLockDisabled()) {
1583                 mWmState.waitFor(state -> state.getKeyguardControllerState().keyguardShowing,
1584                         "Keyguard showing");
1585             }
1586             return this;
1587         }
1588 
1589         LockScreenSession wakeUpDevice() {
1590             pressWakeupButton();
1591             return this;
1592         }
1593 
1594         public LockScreenSession unlockDevice() {
1595             // Make sure the unlock button event is send to the default display.
1596             touchAndCancelOnDisplayCenterSync(DEFAULT_DISPLAY);
1597 
1598             pressUnlockButton();
1599             return this;
1600         }
1601 
1602         public LockScreenSession gotoKeyguard(ComponentName... showWhenLockedActivities) {
1603             if (DEBUG && isLockDisabled()) {
1604                 logE("LockScreenSession.gotoKeyguard() is called without lock enabled.");
1605             }
1606             sleepDevice();
1607             wakeUpDevice();
1608             if (showWhenLockedActivities.length == 0) {
1609                 mWmState.waitForKeyguardShowingAndNotOccluded();
1610             } else {
1611                 mWmState.waitForValidState(showWhenLockedActivities);
1612             }
1613             return this;
1614         }
1615 
1616         @Override
1617         public void close() {
1618             if (mRemoveActivitiesOnClose) {
1619                 removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
1620             }
1621 
1622             setLockDisabled(mIsLockDisabled);
1623             final boolean wasCredentialSet = mLockCredentialSet;
1624             boolean wasDeviceLocked = false;
1625             if (mLockCredentialSet) {
1626                 wasDeviceLocked = mKm != null && mKm.isDeviceLocked();
1627                 removeLockCredential();
1628                 mLockCredentialSet = false;
1629             }
1630 
1631             // Dismiss active keyguard after credential is cleared, so keyguard doesn't ask for
1632             // the stale credential.
1633             // TODO (b/112015010) If keyguard is occluded, credential cannot be removed as expected.
1634             // LockScreenSession#close is always called before stopping all test activities,
1635             // which could cause the keyguard to stay occluded after wakeup.
1636             // If Keyguard is occluded, pressing the back key can hide the ShowWhenLocked activity.
1637             wakeUpDevice();
1638             pressBackButton();
1639 
1640             // If the credential wasn't set, the steps for restoring can be simpler.
1641             if (!wasCredentialSet) {
1642                 mWmState.computeState();
1643                 if (WindowManagerStateHelper.isKeyguardShowingAndNotOccluded(mWmState)) {
1644                     // Keyguard is showing and not occluded so only need to unlock.
1645                     unlockDevice();
1646                     return;
1647                 }
1648 
1649                 final ComponentName home = mWmState.getHomeActivityName();
1650                 if (home != null && mWmState.hasActivityState(home, STATE_RESUMED)) {
1651                     // Home is resumed so nothing to do (e.g. after finishing show-when-locked app).
1652                     return;
1653                 }
1654             }
1655 
1656             // If device is unlocked, there might have ShowWhenLocked activity runs on,
1657             // use home key to clear all activity at foreground.
1658             pressHomeButton();
1659             if (wasDeviceLocked) {
1660                 // The removal of credential needs an extra cycle to take effect.
1661                 sleepDevice();
1662                 wakeUpDevice();
1663             }
1664             if (isKeyguardLocked()) {
1665                 unlockDevice();
1666             }
1667         }
1668 
1669         /**
1670          * Returns whether the lock screen is disabled.
1671          *
1672          * @return true if the lock screen is disabled, false otherwise.
1673          */
1674         private boolean isLockDisabled() {
1675             final String isLockDisabled = runCommandAndPrintOutput(
1676                     "locksettings get-disabled").trim();
1677             return !"null".equals(isLockDisabled) && Boolean.parseBoolean(isLockDisabled);
1678         }
1679 
1680         /**
1681          * Disable the lock screen.
1682          *
1683          * @param lockDisabled true if should disable, false otherwise.
1684          */
1685         protected void setLockDisabled(boolean lockDisabled) {
1686             runCommandAndPrintOutput("locksettings set-disabled " + lockDisabled);
1687         }
1688     }
1689 
1690     /** Helper class to set and restore appop mode "android:system_alert_window". */
1691     protected static class SystemAlertWindowAppOpSession implements AutoCloseable {
1692         private final String mPackageName;
1693         private final int mPreviousOpMode;
1694 
1695         SystemAlertWindowAppOpSession(String packageName, int mode) {
1696             mPackageName = packageName;
1697             try {
1698                 mPreviousOpMode = AppOpsUtils.getOpMode(mPackageName, OPSTR_SYSTEM_ALERT_WINDOW);
1699             } catch (IOException e) {
1700                 throw new RuntimeException(e);
1701             }
1702             setOpMode(mode);
1703         }
1704 
1705         @Override
1706         public void close() {
1707             setOpMode(mPreviousOpMode);
1708         }
1709 
1710         void setOpMode(int mode) {
1711             try {
1712                 AppOpsUtils.setOpMode(mPackageName, OPSTR_SYSTEM_ALERT_WINDOW, mode);
1713             } catch (IOException e) {
1714                 throw new RuntimeException(e);
1715             }
1716         }
1717     }
1718 
1719     protected class AodSession extends SettingsSession<Integer> {
1720         private AmbientDisplayConfiguration mConfig;
1721 
1722         AodSession() {
1723             super(Settings.Secure.getUriFor(Settings.Secure.DOZE_ALWAYS_ON),
1724                     Settings.Secure::getInt,
1725                     Settings.Secure::putInt);
1726             mConfig = new AmbientDisplayConfiguration(mContext);
1727         }
1728 
1729         boolean isAodAvailable() {
1730             return mConfig.alwaysOnAvailable();
1731         }
1732 
1733         void setAodEnabled(boolean enabled) {
1734             set(enabled ? 1 : 0);
1735         }
1736     }
1737 
1738     protected class DevEnableNonResizableMultiWindowSession extends SettingsSession<Integer> {
1739         DevEnableNonResizableMultiWindowSession() {
1740             super(Settings.Global.getUriFor(
1741                     Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW),
1742                     (cr, name) -> Settings.Global.getInt(cr, name, 0 /* def */),
1743                     Settings.Global::putInt);
1744         }
1745     }
1746 
1747     /** Helper class to save, set & wait, and restore rotation related preferences. */
1748     protected class RotationSession extends SettingsSession<Integer> {
1749         private final String FIXED_TO_USER_ROTATION_COMMAND =
1750                 "cmd window fixed-to-user-rotation ";
1751         private final SettingsSession<Integer> mAccelerometerRotation;
1752         private final HandlerThread mThread;
1753         private final Handler mRunnableHandler;
1754         private final SettingsObserver mRotationObserver;
1755         private int mPreviousDegree;
1756         private String mPreviousFixedToUserRotationMode;
1757 
1758         public RotationSession() {
1759             // Save user_rotation and accelerometer_rotation preferences.
1760             super(Settings.System.getUriFor(Settings.System.USER_ROTATION),
1761                     Settings.System::getInt, Settings.System::putInt);
1762             mAccelerometerRotation = new SettingsSession<>(
1763                     Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
1764                     Settings.System::getInt, Settings.System::putInt);
1765 
1766             mThread = new HandlerThread("Observer_Thread");
1767             mThread.start();
1768             mRunnableHandler = new Handler(mThread.getLooper());
1769             mRotationObserver = new SettingsObserver(mRunnableHandler);
1770 
1771             // Disable fixed to user rotation
1772             mPreviousFixedToUserRotationMode = executeShellCommand(FIXED_TO_USER_ROTATION_COMMAND);
1773             executeShellCommand(FIXED_TO_USER_ROTATION_COMMAND + "disabled");
1774 
1775             mPreviousDegree = get();
1776             // Disable accelerometer_rotation.
1777             mAccelerometerRotation.set(0);
1778         }
1779 
1780         @Override
1781         public void set(@NonNull Integer value) {
1782             set(value, true /* waitDeviceRotation */);
1783         }
1784 
1785         /**
1786          * Sets the rotation preference.
1787          *
1788          * @param value The rotation between {@link android.view.Surface#ROTATION_0} ~
1789          *              {@link android.view.Surface#ROTATION_270}
1790          * @param waitDeviceRotation If {@code true}, it will wait until the display has applied the
1791          *                           rotation. Otherwise it only waits for the settings value has
1792          *                           been changed.
1793          */
1794         public void set(@NonNull Integer value, boolean waitDeviceRotation) {
1795             // When the rotation is locked and the SystemUI receives the rotation becoming 0deg, it
1796             // will call freezeRotation to WMS, which will cause USER_ROTATION be set to zero again.
1797             // In order to prevent our test target from being overwritten by SystemUI during
1798             // rotation test, wait for the USER_ROTATION changed then continue testing.
1799             final boolean waitSystemUI = value == ROTATION_0 && mPreviousDegree != ROTATION_0;
1800             final boolean observeRotationSettings = waitSystemUI || !waitDeviceRotation;
1801             if (observeRotationSettings) {
1802                 mRotationObserver.observe();
1803             }
1804             super.set(value);
1805             mPreviousDegree = value;
1806 
1807             if (waitSystemUI) {
1808                 Condition.waitFor(new Condition<>("rotation notified",
1809                         // There will receive USER_ROTATION changed twice because when the device
1810                         // rotates to 0deg, RotationContextButton will also set ROTATION_0 again.
1811                         () -> mRotationObserver.count == 2).setRetryIntervalMs(500));
1812             }
1813 
1814             if (waitDeviceRotation) {
1815                 // Wait for the display to apply the rotation.
1816                 mWmState.waitForRotation(value);
1817             } else {
1818                 // Wait for the settings have been changed.
1819                 Condition.waitFor(new Condition<>("rotation setting changed",
1820                         () -> mRotationObserver.count > 0).setRetryIntervalMs(100));
1821             }
1822 
1823             if (observeRotationSettings) {
1824                 mRotationObserver.stopObserver();
1825             }
1826         }
1827 
1828         @Override
1829         public void close() {
1830             // Restore fixed to user rotation to default
1831             executeShellCommand(FIXED_TO_USER_ROTATION_COMMAND + mPreviousFixedToUserRotationMode);
1832             mThread.quitSafely();
1833             super.close();
1834             // Restore accelerometer_rotation preference.
1835             mAccelerometerRotation.close();
1836             mWaitForRotationOnTearDown = true;
1837         }
1838 
1839         private class SettingsObserver extends ContentObserver {
1840             int count;
1841 
1842             SettingsObserver(Handler handler) { super(handler); }
1843 
1844             void observe() {
1845                 count = 0;
1846                 final ContentResolver resolver = mContext.getContentResolver();
1847                 resolver.registerContentObserver(Settings.System.getUriFor(
1848                         Settings.System.USER_ROTATION), false, this);
1849             }
1850 
1851             void stopObserver() {
1852                 count = 0;
1853                 final ContentResolver resolver = mContext.getContentResolver();
1854                 resolver.unregisterContentObserver(this);
1855             }
1856 
1857             @Override
1858             public void onChange(boolean selfChange) {
1859                 count++;
1860             }
1861         }
1862     }
1863 
1864     /** Helper class to save, set, and restore font_scale preferences. */
1865     protected static class FontScaleSession extends SettingsSession<Float> {
1866         FontScaleSession() {
1867             super(Settings.System.getUriFor(Settings.System.FONT_SCALE),
1868                     Settings.System::getFloat,
1869                     Settings.System::putFloat);
1870         }
1871 
1872         @Override
1873         public Float get() {
1874             Float value = super.get();
1875             return value == null ? 1f : value;
1876         }
1877     }
1878 
1879     /**
1880      * Returns whether the test device respects settings of locked user rotation mode.
1881      *
1882      * The method sets the locked user rotation settings to the rotation that rotates the display by
1883      * 180 degrees and checks if the actual display rotation changes after that.
1884      *
1885      * This is a necessary assumption check before leveraging user rotation mode to force display
1886      * rotation, because there is no requirement that an Android device that supports both
1887      * orientations needs to support user rotation mode.
1888      *
1889      * @param session   the rotation session used to set user rotation
1890      * @param displayId the display ID to check rotation against
1891      * @return {@code true} if test device respects settings of locked user rotation mode;
1892      * {@code false} if not.
1893      */
1894     protected boolean supportsLockedUserRotation(RotationSession session, int displayId) {
1895         final int origRotation = getDeviceRotation(displayId);
1896         // Use the same orientation as target rotation to avoid affect of app-requested orientation.
1897         final int targetRotation = (origRotation + 2) % 4;
1898         session.set(targetRotation);
1899         final boolean result = (getDeviceRotation(displayId) == targetRotation);
1900         session.set(origRotation);
1901         return result;
1902     }
1903 
1904     protected int getDeviceRotation(int displayId) {
1905         final String displays = runCommandAndPrintOutput("dumpsys display displays").trim();
1906         Pattern pattern = Pattern.compile(
1907                 "(mDisplayId=" + displayId + ")([\\s\\S]*?)(mOverrideDisplayInfo)(.*)"
1908                         + "(rotation)(\\s+)(\\d+)");
1909         Matcher matcher = pattern.matcher(displays);
1910         if (matcher.find()) {
1911             final String match = matcher.group(7);
1912             return Integer.parseInt(match);
1913         }
1914 
1915         return INVALID_DEVICE_ROTATION;
1916     }
1917 
1918     /**
1919      * Creates a {#link ActivitySessionClient} instance with instrumentation context. It is used
1920      * when the caller doen't need try-with-resource.
1921      */
1922     public static ActivitySessionClient createActivitySessionClient() {
1923         return new ActivitySessionClient(getInstrumentation().getContext());
1924     }
1925 
1926     /** Empties the test journal so the following events won't be mixed-up with previous records. */
1927     protected void separateTestJournal() {
1928         TestJournalContainer.start();
1929     }
1930 
1931     protected static String runCommandAndPrintOutput(String command) {
1932         final String output = executeShellCommand(command);
1933         log(output);
1934         return output;
1935     }
1936 
1937     protected static class LogSeparator {
1938         private final String mUniqueString;
1939 
1940         private LogSeparator() {
1941             mUniqueString = UUID.randomUUID().toString();
1942         }
1943 
1944         @Override
1945         public String toString() {
1946             return mUniqueString;
1947         }
1948     }
1949 
1950     /**
1951      * Inserts a log separator so we can always find the starting point from where to evaluate
1952      * following logs.
1953      *
1954      * @return Unique log separator.
1955      */
1956     protected LogSeparator separateLogs() {
1957         final LogSeparator logSeparator = new LogSeparator();
1958         executeShellCommand("log -t " + LOG_SEPARATOR + " " + logSeparator);
1959         EventLog.writeEvent(EVENT_LOG_SEPARATOR_TAG, logSeparator.mUniqueString);
1960         return logSeparator;
1961     }
1962 
1963     protected static String[] getDeviceLogsForComponents(
1964             LogSeparator logSeparator, String... logTags) {
1965         String filters = LOG_SEPARATOR + ":I ";
1966         for (String component : logTags) {
1967             filters += component + ":I ";
1968         }
1969         final String[] result = executeShellCommand("logcat -v brief -d " + filters + " *:S")
1970                 .split("\\n");
1971         if (logSeparator == null) {
1972             return result;
1973         }
1974 
1975         // Make sure that we only check logs after the separator.
1976         int i = 0;
1977         boolean lookingForSeparator = true;
1978         while (i < result.length && lookingForSeparator) {
1979             if (result[i].contains(logSeparator.toString())) {
1980                 lookingForSeparator = false;
1981             }
1982             i++;
1983         }
1984         final String[] filteredResult = new String[result.length - i];
1985         for (int curPos = 0; i < result.length; curPos++, i++) {
1986             filteredResult[curPos] = result[i];
1987         }
1988         return filteredResult;
1989     }
1990 
1991     protected static List<Event> getEventLogsForComponents(LogSeparator logSeparator, int... tags) {
1992         List<Event> events = new ArrayList<>();
1993 
1994         int[] searchTags = Arrays.copyOf(tags, tags.length + 1);
1995         searchTags[searchTags.length - 1] = EVENT_LOG_SEPARATOR_TAG;
1996 
1997         try {
1998             EventLog.readEvents(searchTags, events);
1999         } catch (IOException e) {
2000             fail("Could not read from event log." + e);
2001         }
2002 
2003         for (Iterator<Event> itr = events.iterator(); itr.hasNext(); ) {
2004             Event event = itr.next();
2005             itr.remove();
2006             if (event.getTag() == EVENT_LOG_SEPARATOR_TAG &&
2007                     logSeparator.mUniqueString.equals(event.getData())) {
2008                 break;
2009             }
2010         }
2011         return events;
2012     }
2013 
2014     protected boolean supportsMultiDisplay() {
2015         return mContext.getPackageManager().hasSystemFeature(
2016                 FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
2017     }
2018 
2019     protected boolean supportsInstallableIme() {
2020         return mContext.getPackageManager().hasSystemFeature(FEATURE_INPUT_METHODS);
2021     }
2022 
2023     static class CountSpec<T> {
2024         static final int DONT_CARE = Integer.MIN_VALUE;
2025         static final int EQUALS = 1;
2026         static final int GREATER_THAN = 2;
2027         static final int LESS_THAN = 3;
2028         static final int GREATER_THAN_OR_EQUALS = 4;
2029 
2030         final T mEvent;
2031         final int mRule;
2032         final int mCount;
2033         final String mMessage;
2034 
2035         CountSpec(T event, int rule, int count, String message) {
2036             mEvent = event;
2037             mRule = count == DONT_CARE ? DONT_CARE : rule;
2038             mCount = count;
2039             if (message != null) {
2040                 mMessage = message;
2041             } else {
2042                 switch (rule) {
2043                     case EQUALS:
2044                         mMessage = event + " must equal to " + count;
2045                         break;
2046                     case GREATER_THAN:
2047                         mMessage = event + " must be greater than " + count;
2048                         break;
2049                     case LESS_THAN:
2050                         mMessage = event + " must be less than " + count;
2051                         break;
2052                     case GREATER_THAN_OR_EQUALS:
2053                         mMessage = event + " must be greater than (or equals to) " + count;
2054                         break;
2055                     default:
2056                         mMessage = "Don't care";
2057                 }
2058             }
2059         }
2060 
2061         /** @return {@code true} if the given value is satisfied the condition. */
2062         boolean validate(int value) {
2063             switch (mRule) {
2064                 case DONT_CARE:
2065                     return true;
2066                 case EQUALS:
2067                     return value == mCount;
2068                 case GREATER_THAN:
2069                     return value > mCount;
2070                 case LESS_THAN:
2071                     return value < mCount;
2072                 case GREATER_THAN_OR_EQUALS:
2073                     return value >= mCount;
2074                 default:
2075             }
2076             throw new RuntimeException("Unknown CountSpec rule");
2077         }
2078     }
2079 
2080     static <T> CountSpec<T> countSpec(T event, int rule, int count, String message) {
2081         return new CountSpec<>(event, rule, count, message);
2082     }
2083 
2084     static <T> CountSpec<T> countSpec(T event, int rule, int count) {
2085         return new CountSpec<>(event, rule, count, null /* message */);
2086     }
2087 
2088     static void assertLifecycleCounts(ComponentName activityName, String message,
2089             int createCount, int startCount, int resumeCount, int pauseCount, int stopCount,
2090             int destroyCount, int configChangeCount) {
2091         new ActivityLifecycleCounts(activityName).assertCountWithRetry(
2092                 message,
2093                 countSpec(ActivityCallback.ON_CREATE, CountSpec.EQUALS, createCount),
2094                 countSpec(ActivityCallback.ON_START, CountSpec.EQUALS, startCount),
2095                 countSpec(ActivityCallback.ON_RESUME, CountSpec.EQUALS, resumeCount),
2096                 countSpec(ActivityCallback.ON_PAUSE, CountSpec.EQUALS, pauseCount),
2097                 countSpec(ActivityCallback.ON_STOP, CountSpec.EQUALS, stopCount),
2098                 countSpec(ActivityCallback.ON_DESTROY, CountSpec.EQUALS, destroyCount),
2099                 countSpec(ActivityCallback.ON_CONFIGURATION_CHANGED, CountSpec.EQUALS,
2100                         configChangeCount));
2101     }
2102 
2103     static void assertLifecycleCounts(ComponentName activityName,
2104             int createCount, int startCount, int resumeCount, int pauseCount, int stopCount,
2105             int destroyCount, int configChangeCount) {
2106         assertLifecycleCounts(activityName, "Assert lifecycle of " + getLogTag(activityName),
2107                 createCount, startCount, resumeCount, pauseCount, stopCount,
2108                 destroyCount, configChangeCount);
2109     }
2110 
2111     static void assertSingleLaunch(ComponentName activityName) {
2112         assertLifecycleCounts(activityName,
2113                 "activity create, start, and resume",
2114                 1 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
2115                 0 /* pauseCount */, 0 /* stopCount */, 0 /* destroyCount */,
2116                 CountSpec.DONT_CARE /* configChangeCount */);
2117     }
2118 
2119     static void assertSingleLaunchAndStop(ComponentName activityName) {
2120         assertLifecycleCounts(activityName,
2121                 "activity create, start, resume, pause, and stop",
2122                 1 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
2123                 1 /* pauseCount */, 1 /* stopCount */, 0 /* destroyCount */,
2124                 CountSpec.DONT_CARE /* configChangeCount */);
2125     }
2126 
2127     static void assertSingleStartAndStop(ComponentName activityName) {
2128         assertLifecycleCounts(activityName,
2129                 "activity start, resume, pause, and stop",
2130                 0 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
2131                 1 /* pauseCount */, 1 /* stopCount */, 0 /* destroyCount */,
2132                 CountSpec.DONT_CARE /* configChangeCount */);
2133     }
2134 
2135     static void assertSingleStart(ComponentName activityName) {
2136         assertLifecycleCounts(activityName,
2137                 "activity start and resume",
2138                 0 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
2139                 0 /* pauseCount */, 0 /* stopCount */, 0 /* destroyCount */,
2140                 CountSpec.DONT_CARE /* configChangeCount */);
2141     }
2142 
2143     /** Assert the activity is either relaunched or received configuration changed. */
2144     static void assertActivityLifecycle(ComponentName activityName, boolean relaunched) {
2145         Condition.<String>waitForResult(
2146                 activityName + (relaunched ? " relaunched" : " config changed"),
2147                 condition -> condition
2148                 .setResultSupplier(() -> checkActivityIsRelaunchedOrConfigurationChanged(
2149                         getActivityName(activityName),
2150                         TestJournalContainer.get(activityName).callbacks, relaunched))
2151                 .setResultValidator(failedReasons -> failedReasons == null)
2152                 .setOnFailure(failedReasons -> fail(failedReasons)));
2153     }
2154 
2155     /** Assert the activity is either relaunched or received configuration changed. */
2156     static List<ActivityCallback> assertActivityLifecycle(ActivitySession activitySession,
2157             boolean relaunched) {
2158         final String name = activitySession.getName().flattenToShortString();
2159         final List<ActivityCallback> callbackHistory = activitySession.takeCallbackHistory();
2160         String failedReason = checkActivityIsRelaunchedOrConfigurationChanged(
2161                 name, callbackHistory, relaunched);
2162         if (failedReason != null) {
2163             fail(failedReason);
2164         }
2165         return callbackHistory;
2166     }
2167 
2168     private static String checkActivityIsRelaunchedOrConfigurationChanged(String name,
2169             List<ActivityCallback> callbackHistory, boolean relaunched) {
2170         final ActivityLifecycleCounts lifecycles = new ActivityLifecycleCounts(callbackHistory);
2171         if (relaunched) {
2172             return lifecycles.validateCount(
2173                     countSpec(ActivityCallback.ON_DESTROY, CountSpec.GREATER_THAN, 0,
2174                             name + " must have been destroyed."),
2175                     countSpec(ActivityCallback.ON_CREATE, CountSpec.GREATER_THAN, 0,
2176                             name + " must have been (re)created."));
2177         }
2178         return lifecycles.validateCount(
2179                 countSpec(ActivityCallback.ON_DESTROY, CountSpec.LESS_THAN, 1,
2180                         name + " must *NOT* have been destroyed."),
2181                 countSpec(ActivityCallback.ON_CREATE, CountSpec.LESS_THAN, 1,
2182                         name + " must *NOT* have been (re)created."),
2183                 countSpec(ActivityCallback.ON_CONFIGURATION_CHANGED, CountSpec.GREATER_THAN, 0,
2184                                 name + " must have received configuration changed."));
2185     }
2186 
2187     static void assertRelaunchOrConfigChanged(ComponentName activityName, int numRelaunch,
2188             int numConfigChange) {
2189         new ActivityLifecycleCounts(activityName).assertCountWithRetry("relaunch or config changed",
2190                 countSpec(ActivityCallback.ON_DESTROY, CountSpec.EQUALS, numRelaunch),
2191                 countSpec(ActivityCallback.ON_CREATE, CountSpec.EQUALS, numRelaunch),
2192                 countSpec(ActivityCallback.ON_CONFIGURATION_CHANGED, CountSpec.EQUALS,
2193                         numConfigChange));
2194     }
2195 
2196     static void assertActivityDestroyed(ComponentName activityName) {
2197         new ActivityLifecycleCounts(activityName).assertCountWithRetry("activity destroyed",
2198                 countSpec(ActivityCallback.ON_DESTROY, CountSpec.EQUALS, 1),
2199                 countSpec(ActivityCallback.ON_CREATE, CountSpec.EQUALS, 0),
2200                 countSpec(ActivityCallback.ON_CONFIGURATION_CHANGED, CountSpec.EQUALS, 0));
2201     }
2202 
2203     static void assertSecurityExceptionFromActivityLauncher() {
2204         waitForOrFail("SecurityException from " + ActivityLauncher.TAG,
2205                 ActivityLauncher::hasCaughtSecurityException);
2206     }
2207 
2208     private static final Pattern sCurrentUiModePattern = Pattern.compile("mCurUiMode=0x(\\d+)");
2209     private static final Pattern sUiModeLockedPattern =
2210             Pattern.compile("mUiModeLocked=(true|false)");
2211 
2212     @NonNull
2213     SizeInfo getLastReportedSizesForActivity(ComponentName activityName) {
2214         return Condition.waitForResult("sizes of " + activityName + " to be reported",
2215                 condition -> condition.setResultSupplier(() -> {
2216                     final ConfigInfo info = TestJournalContainer.get(activityName).lastConfigInfo;
2217                     return info != null ? info.sizeInfo : null;
2218                 }).setResultValidator(Objects::nonNull).setOnFailure(unusedResult ->
2219                         fail("No config reported from " + activityName)));
2220     }
2221 
2222     /** Check if a device has display cutout. */
2223     boolean hasDisplayCutout() {
2224         // Launch an activity to report cutout state
2225         separateTestJournal();
2226         launchActivity(BROADCAST_RECEIVER_ACTIVITY);
2227 
2228         // Read the logs to check if cutout is present
2229         final Boolean displayCutoutPresent = getCutoutStateForActivity(BROADCAST_RECEIVER_ACTIVITY);
2230         assertNotNull("The activity should report cutout state", displayCutoutPresent);
2231 
2232         // Finish activity
2233         mBroadcastActionTrigger.finishBroadcastReceiverActivity();
2234         mWmState.waitForWithAmState(
2235                 (state) -> !state.containsActivity(BROADCAST_RECEIVER_ACTIVITY),
2236                 "activity to be removed");
2237 
2238         return displayCutoutPresent;
2239     }
2240 
2241     /**
2242      * Wait for activity to report cutout state in logs and return it. Will return {@code null}
2243      * after timeout.
2244      */
2245     @Nullable
2246     private Boolean getCutoutStateForActivity(ComponentName activityName) {
2247         return Condition.waitForResult("cutout state to be reported", condition -> condition
2248                 .setResultSupplier(() -> {
2249                     final Bundle extras = TestJournalContainer.get(activityName).extras;
2250                     return extras.containsKey(EXTRA_CUTOUT_EXISTS)
2251                             ? extras.getBoolean(EXTRA_CUTOUT_EXISTS)
2252                             : null;
2253                 }).setResultValidator(cutoutExists -> cutoutExists != null));
2254     }
2255 
2256     /** Waits for at least one onMultiWindowModeChanged event. */
2257     ActivityLifecycleCounts waitForOnMultiWindowModeChanged(ComponentName activityName) {
2258         final ActivityLifecycleCounts counts = new ActivityLifecycleCounts(activityName);
2259         Condition.waitFor(counts.countWithRetry("waitForOnMultiWindowModeChanged", countSpec(
2260                 ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED, CountSpec.GREATER_THAN, 0)));
2261         return counts;
2262     }
2263 
2264     WindowState getPackageWindowState(String packageName) {
2265         final WindowManagerState.WindowState window =
2266                 mWmState.getWindowByPackageName(packageName, TYPE_BASE_APPLICATION);
2267         assertNotNull(window);
2268         return window;
2269     }
2270 
2271     static class ActivityLifecycleCounts {
2272         private final int[] mCounts = new int[ActivityCallback.SIZE];
2273         private final int[] mFirstIndexes = new int[ActivityCallback.SIZE];
2274         private final int[] mLastIndexes = new int[ActivityCallback.SIZE];
2275         private ComponentName mActivityName;
2276 
2277         ActivityLifecycleCounts(ComponentName componentName) {
2278             mActivityName = componentName;
2279             updateCount(TestJournalContainer.get(componentName).callbacks);
2280         }
2281 
2282         ActivityLifecycleCounts(List<ActivityCallback> callbacks) {
2283             updateCount(callbacks);
2284         }
2285 
2286         private void updateCount(List<ActivityCallback> callbacks) {
2287             // The callback list could be from the reference of TestJournal. If we are counting for
2288             // retrying, there may be new data added to the list from other threads.
2289             TestJournalContainer.withThreadSafeAccess(() -> {
2290                 Arrays.fill(mFirstIndexes, -1);
2291                 for (int i = 0; i < callbacks.size(); i++) {
2292                     final ActivityCallback callback = callbacks.get(i);
2293                     final int ordinal = callback.ordinal();
2294                     mCounts[ordinal]++;
2295                     mLastIndexes[ordinal] = i;
2296                     if (mFirstIndexes[ordinal] == -1) {
2297                         mFirstIndexes[ordinal] = i;
2298                     }
2299                 }
2300             });
2301         }
2302 
2303         int getCount(ActivityCallback callback) {
2304             return mCounts[callback.ordinal()];
2305         }
2306 
2307         int getFirstIndex(ActivityCallback callback) {
2308             return mFirstIndexes[callback.ordinal()];
2309         }
2310 
2311         int getLastIndex(ActivityCallback callback) {
2312             return mLastIndexes[callback.ordinal()];
2313         }
2314 
2315         @SafeVarargs
2316         final Condition<String> countWithRetry(String message,
2317                 CountSpec<ActivityCallback>... countSpecs) {
2318             if (mActivityName == null) {
2319                 throw new IllegalStateException(
2320                         "It is meaningless to retry without specified activity");
2321             }
2322             return new Condition<String>(message)
2323                     .setOnRetry(() -> {
2324                         Arrays.fill(mCounts, 0);
2325                         Arrays.fill(mLastIndexes, 0);
2326                         updateCount(TestJournalContainer.get(mActivityName).callbacks);
2327                     })
2328                     .setResultSupplier(() -> validateCount(countSpecs))
2329                     .setResultValidator(failedReasons -> failedReasons == null);
2330         }
2331 
2332         @SafeVarargs
2333         final void assertCountWithRetry(String message, CountSpec<ActivityCallback>... countSpecs) {
2334             if (mActivityName == null) {
2335                 throw new IllegalStateException(
2336                         "It is meaningless to retry without specified activity");
2337             }
2338             Condition.<String>waitForResult(countWithRetry(message, countSpecs)
2339                     .setOnFailure(failedReasons -> fail(message + ": " + failedReasons)));
2340         }
2341 
2342         @SafeVarargs
2343         final String validateCount(CountSpec<ActivityCallback>... countSpecs) {
2344             ArrayList<String> failedReasons = null;
2345             for (CountSpec<ActivityCallback> spec : countSpecs) {
2346                 final int realCount = mCounts[spec.mEvent.ordinal()];
2347                 if (!spec.validate(realCount)) {
2348                     if (failedReasons == null) {
2349                         failedReasons = new ArrayList<>();
2350                     }
2351                     failedReasons.add(spec.mMessage + " (got " + realCount + ")");
2352                 }
2353             }
2354             return failedReasons == null ? null : String.join("\n", failedReasons);
2355         }
2356     }
2357 
2358     protected void stopTestPackage(final String packageName) {
2359         runWithShellPermission(() -> mAm.forceStopPackage(packageName));
2360     }
2361 
2362     protected LaunchActivityBuilder getLaunchActivityBuilder() {
2363         return new LaunchActivityBuilder(mWmState);
2364     }
2365 
2366     public static <T extends Activity>
2367     ActivityScenarioRule<T> createFullscreenActivityScenarioRule(Class<T> clazz) {
2368         final ActivityOptions options = ActivityOptions.makeBasic();
2369         options.setLaunchWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
2370         return new ActivityScenarioRule<>(clazz, options.toBundle());
2371     }
2372 
2373     protected static class LaunchActivityBuilder implements LaunchProxy {
2374         private final WindowManagerStateHelper mAmWmState;
2375 
2376         // The activity to be launched
2377         private ComponentName mTargetActivity = TEST_ACTIVITY;
2378         private boolean mUseApplicationContext;
2379         private boolean mToSide;
2380         private boolean mRandomData;
2381         private boolean mNewTask;
2382         private boolean mMultipleTask;
2383         private boolean mAllowMultipleInstances = true;
2384         private boolean mLaunchTaskBehind;
2385         private boolean mFinishBeforeLaunch;
2386         private int mDisplayId = INVALID_DISPLAY;
2387         private int mWindowingMode = -1;
2388         private int mActivityType = ACTIVITY_TYPE_UNDEFINED;
2389         // A proxy activity that launches other activities including mTargetActivityName
2390         private ComponentName mLaunchingActivity = LAUNCHING_ACTIVITY;
2391         private boolean mReorderToFront;
2392         private boolean mWaitForLaunched;
2393         private boolean mSuppressExceptions;
2394         private boolean mWithShellPermission;
2395         // Use of the following variables indicates that a broadcast receiver should be used instead
2396         // of a launching activity;
2397         private ComponentName mBroadcastReceiver;
2398         private String mBroadcastReceiverAction;
2399         private int mIntentFlags;
2400         private Bundle mExtras;
2401         private LaunchInjector mLaunchInjector;
2402         private ActivitySessionClient mActivitySessionClient;
2403 
2404         private enum LauncherType {
2405             INSTRUMENTATION, LAUNCHING_ACTIVITY, BROADCAST_RECEIVER
2406         }
2407 
2408         private LauncherType mLauncherType = LauncherType.LAUNCHING_ACTIVITY;
2409 
2410         public LaunchActivityBuilder(WindowManagerStateHelper amWmState) {
2411             mAmWmState = amWmState;
2412             mWaitForLaunched = true;
2413             mWithShellPermission = true;
2414         }
2415 
2416         public LaunchActivityBuilder setToSide(boolean toSide) {
2417             mToSide = toSide;
2418             return this;
2419         }
2420 
2421         public LaunchActivityBuilder setRandomData(boolean randomData) {
2422             mRandomData = randomData;
2423             return this;
2424         }
2425 
2426         public LaunchActivityBuilder setNewTask(boolean newTask) {
2427             mNewTask = newTask;
2428             return this;
2429         }
2430 
2431         public LaunchActivityBuilder setMultipleTask(boolean multipleTask) {
2432             mMultipleTask = multipleTask;
2433             return this;
2434         }
2435 
2436         public LaunchActivityBuilder allowMultipleInstances(boolean allowMultipleInstances) {
2437             mAllowMultipleInstances = allowMultipleInstances;
2438             return this;
2439         }
2440 
2441         public LaunchActivityBuilder setLaunchTaskBehind(boolean launchTaskBehind) {
2442             mLaunchTaskBehind = launchTaskBehind;
2443             return this;
2444         }
2445 
2446         public LaunchActivityBuilder setReorderToFront(boolean reorderToFront) {
2447             mReorderToFront = reorderToFront;
2448             return this;
2449         }
2450 
2451         public LaunchActivityBuilder setUseApplicationContext(boolean useApplicationContext) {
2452             mUseApplicationContext = useApplicationContext;
2453             return this;
2454         }
2455 
2456         public LaunchActivityBuilder setFinishBeforeLaunch(boolean finishBeforeLaunch) {
2457             mFinishBeforeLaunch = finishBeforeLaunch;
2458             return this;
2459         }
2460 
2461         public ComponentName getTargetActivity() {
2462             return mTargetActivity;
2463         }
2464 
2465         public boolean isTargetActivityTranslucent() {
2466             return mAmWmState.isActivityTranslucent(mTargetActivity);
2467         }
2468 
2469         public LaunchActivityBuilder setTargetActivity(ComponentName targetActivity) {
2470             mTargetActivity = targetActivity;
2471             return this;
2472         }
2473 
2474         public LaunchActivityBuilder setDisplayId(int id) {
2475             mDisplayId = id;
2476             return this;
2477         }
2478 
2479         public LaunchActivityBuilder setWindowingMode(int windowingMode) {
2480             mWindowingMode = windowingMode;
2481             return this;
2482         }
2483 
2484         public LaunchActivityBuilder setActivityType(int type) {
2485             mActivityType = type;
2486             return this;
2487         }
2488 
2489         public LaunchActivityBuilder setLaunchingActivity(ComponentName launchingActivity) {
2490             mLaunchingActivity = launchingActivity;
2491             mLauncherType = LauncherType.LAUNCHING_ACTIVITY;
2492             return this;
2493         }
2494 
2495         public LaunchActivityBuilder setWaitForLaunched(boolean shouldWait) {
2496             mWaitForLaunched = shouldWait;
2497             return this;
2498         }
2499 
2500         /** Use broadcast receiver as a launchpad for activities. */
2501         public LaunchActivityBuilder setUseBroadcastReceiver(final ComponentName broadcastReceiver,
2502                 final String broadcastAction) {
2503             mBroadcastReceiver = broadcastReceiver;
2504             mBroadcastReceiverAction = broadcastAction;
2505             mLauncherType = LauncherType.BROADCAST_RECEIVER;
2506             return this;
2507         }
2508 
2509         /** Use {@link android.app.Instrumentation} as a launchpad for activities. */
2510         public LaunchActivityBuilder setUseInstrumentation() {
2511             mLauncherType = LauncherType.INSTRUMENTATION;
2512             // Calling startActivity() from outside of an Activity context requires the
2513             // FLAG_ACTIVITY_NEW_TASK flag.
2514             setNewTask(true);
2515             return this;
2516         }
2517 
2518         public LaunchActivityBuilder setSuppressExceptions(boolean suppress) {
2519             mSuppressExceptions = suppress;
2520             return this;
2521         }
2522 
2523         public LaunchActivityBuilder setWithShellPermission(boolean withShellPermission) {
2524             mWithShellPermission = withShellPermission;
2525             return this;
2526         }
2527 
2528         public LaunchActivityBuilder setActivitySessionClient(ActivitySessionClient sessionClient) {
2529             mActivitySessionClient = sessionClient;
2530             return this;
2531         }
2532 
2533         @Override
2534         public boolean shouldWaitForLaunched() {
2535             return mWaitForLaunched;
2536         }
2537 
2538         public LaunchActivityBuilder setIntentFlags(int flags) {
2539             mIntentFlags = flags;
2540             return this;
2541         }
2542 
2543         public LaunchActivityBuilder setIntentExtra(Consumer<Bundle> extrasConsumer) {
2544             if (extrasConsumer != null) {
2545                 mExtras = new Bundle();
2546                 extrasConsumer.accept(mExtras);
2547             }
2548             return this;
2549         }
2550 
2551         @Override
2552         public Bundle getExtras() {
2553             return mExtras;
2554         }
2555 
2556         @Override
2557         public void setLaunchInjector(LaunchInjector injector) {
2558             mLaunchInjector = injector;
2559         }
2560 
2561         @Override
2562         public void execute() {
2563             if (mActivitySessionClient != null) {
2564                 final ActivitySessionClient client = mActivitySessionClient;
2565                 // Clear the session client so its startActivity can call the real execute().
2566                 mActivitySessionClient = null;
2567                 client.startActivity(this);
2568                 return;
2569             }
2570             switch (mLauncherType) {
2571                 case INSTRUMENTATION:
2572                     if (mWithShellPermission) {
2573                         NestedShellPermission.run(this::launchUsingInstrumentation);
2574                     } else {
2575                         launchUsingInstrumentation();
2576                     }
2577                     break;
2578                 case LAUNCHING_ACTIVITY:
2579                 case BROADCAST_RECEIVER:
2580                     launchUsingShellCommand();
2581             }
2582 
2583             if (mWaitForLaunched) {
2584                 mAmWmState.waitForValidState(mTargetActivity);
2585             }
2586         }
2587 
2588         /** Launch an activity using instrumentation. */
2589         private void launchUsingInstrumentation() {
2590             final Bundle b = new Bundle();
2591             b.putBoolean(KEY_LAUNCH_ACTIVITY, true);
2592             b.putBoolean(KEY_LAUNCH_TO_SIDE, mToSide);
2593             b.putBoolean(KEY_RANDOM_DATA, mRandomData);
2594             b.putBoolean(KEY_NEW_TASK, mNewTask);
2595             b.putBoolean(KEY_MULTIPLE_TASK, mMultipleTask);
2596             b.putBoolean(KEY_MULTIPLE_INSTANCES, mAllowMultipleInstances);
2597             b.putBoolean(KEY_LAUNCH_TASK_BEHIND, mLaunchTaskBehind);
2598             b.putBoolean(KEY_REORDER_TO_FRONT, mReorderToFront);
2599             b.putInt(KEY_DISPLAY_ID, mDisplayId);
2600             b.putInt(KEY_WINDOWING_MODE, mWindowingMode);
2601             b.putInt(KEY_ACTIVITY_TYPE, mActivityType);
2602             b.putBoolean(KEY_USE_APPLICATION_CONTEXT, mUseApplicationContext);
2603             b.putString(KEY_TARGET_COMPONENT, getActivityName(mTargetActivity));
2604             b.putBoolean(KEY_SUPPRESS_EXCEPTIONS, mSuppressExceptions);
2605             b.putInt(KEY_INTENT_FLAGS, mIntentFlags);
2606             b.putBundle(KEY_INTENT_EXTRAS, getExtras());
2607             final Context context = getInstrumentation().getContext();
2608             launchActivityFromExtras(context, b, mLaunchInjector);
2609         }
2610 
2611         /** Build and execute a shell command to launch an activity. */
2612         private void launchUsingShellCommand() {
2613             StringBuilder commandBuilder = new StringBuilder();
2614             if (mBroadcastReceiver != null && mBroadcastReceiverAction != null) {
2615                 // Use broadcast receiver to launch the target.
2616                 commandBuilder.append("am broadcast -a ").append(mBroadcastReceiverAction)
2617                         .append(" -p ").append(mBroadcastReceiver.getPackageName())
2618                         // Include stopped packages
2619                         .append(" -f 0x00000020");
2620             } else {
2621                 // If new task flag isn't set the windowing mode of launcher activity will be the
2622                 // windowing mode of the target activity, so we need to launch launcher activity in
2623                 // it.
2624                 String amStartCmd =
2625                         (mWindowingMode == -1 || mNewTask)
2626                                 ? getAmStartCmd(mLaunchingActivity)
2627                                 : getAmStartCmd(mLaunchingActivity, mDisplayId)
2628                                         + " --windowingMode " + mWindowingMode;
2629                 // Use launching activity to launch the target.
2630                 commandBuilder.append(amStartCmd)
2631                         .append(" -f 0x20000020");
2632             }
2633 
2634             // Add a flag to ensure we actually mean to launch an activity.
2635             commandBuilder.append(" --ez " + KEY_LAUNCH_ACTIVITY + " true");
2636 
2637             if (mToSide) {
2638                 commandBuilder.append(" --ez " + KEY_LAUNCH_TO_SIDE + " true");
2639             }
2640             if (mRandomData) {
2641                 commandBuilder.append(" --ez " + KEY_RANDOM_DATA + " true");
2642             }
2643             if (mNewTask) {
2644                 commandBuilder.append(" --ez " + KEY_NEW_TASK + " true");
2645             }
2646             if (mMultipleTask) {
2647                 commandBuilder.append(" --ez " + KEY_MULTIPLE_TASK + " true");
2648             }
2649             if (mAllowMultipleInstances) {
2650                 commandBuilder.append(" --ez " + KEY_MULTIPLE_INSTANCES + " true");
2651             }
2652             if (mReorderToFront) {
2653                 commandBuilder.append(" --ez " + KEY_REORDER_TO_FRONT + " true");
2654             }
2655             if (mFinishBeforeLaunch) {
2656                 commandBuilder.append(" --ez " + KEY_FINISH_BEFORE_LAUNCH + " true");
2657             }
2658             if (mDisplayId != INVALID_DISPLAY) {
2659                 commandBuilder.append(" --ei " + KEY_DISPLAY_ID + " ").append(mDisplayId);
2660             }
2661             if (mWindowingMode != -1) {
2662                 commandBuilder.append(" --ei " + KEY_WINDOWING_MODE + " ").append(mWindowingMode);
2663             }
2664             if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
2665                 commandBuilder.append(" --ei " + KEY_ACTIVITY_TYPE + " ").append(mActivityType);
2666             }
2667 
2668             if (mUseApplicationContext) {
2669                 commandBuilder.append(" --ez " + KEY_USE_APPLICATION_CONTEXT + " true");
2670             }
2671 
2672             if (mTargetActivity != null) {
2673                 // {@link ActivityLauncher} parses this extra string by
2674                 // {@link ComponentName#unflattenFromString(String)}.
2675                 commandBuilder.append(" --es " + KEY_TARGET_COMPONENT + " ")
2676                         .append(getActivityName(mTargetActivity));
2677             }
2678 
2679             if (mSuppressExceptions) {
2680                 commandBuilder.append(" --ez " + KEY_SUPPRESS_EXCEPTIONS + " true");
2681             }
2682 
2683             if (mIntentFlags != 0) {
2684                 commandBuilder.append(" --ei " + KEY_INTENT_FLAGS + " ").append(mIntentFlags);
2685             }
2686 
2687             if (mLaunchInjector != null) {
2688                 commandBuilder.append(" --ez " + KEY_FORWARD + " true");
2689                 mLaunchInjector.setupShellCommand(commandBuilder);
2690             }
2691             executeShellCommand(commandBuilder.toString());
2692         }
2693     }
2694 
2695     /**
2696      * The actions which wraps a test method. It is used to set necessary rules that cannot be
2697      * overridden by subclasses. It executes in the outer scope of {@link Before} and {@link After}.
2698      */
2699     protected class WrapperRule implements TestRule {
2700         private final Runnable mBefore;
2701         private final Runnable mAfter;
2702 
2703         protected WrapperRule(Runnable before, Runnable after) {
2704             mBefore = before;
2705             mAfter = after;
2706         }
2707 
2708         @Override
2709         public Statement apply(final Statement base, final Description description) {
2710             return new Statement() {
2711                 @Override
2712                 public void evaluate()  {
2713                     if (mBefore != null) {
2714                         mBefore.run();
2715                     }
2716                     try {
2717                         base.evaluate();
2718                     } catch (Throwable e) {
2719                         mPostAssertionRule.addError(e);
2720                     } finally {
2721                         if (mAfter != null) {
2722                             mAfter.run();
2723                         }
2724                     }
2725                 }
2726             };
2727         }
2728     }
2729 
2730     /**
2731      * The post assertion to ensure all test methods don't violate the generic rule. It is also used
2732      * to collect multiple errors.
2733      */
2734     private class PostAssertionRule extends ErrorCollector {
2735         private Throwable mLastError;
2736 
2737         @Override
2738         protected void verify() throws Throwable {
2739             if (mLastError != null) {
2740                 // Try to recover the bad state of device to avoid subsequent test failures.
2741                 if (isKeyguardLocked()) {
2742                     mLastError.addSuppressed(new IllegalStateException("Keyguard is locked"));
2743                     // To clear the credential immediately, the screen need to be turned on.
2744                     pressWakeupButton();
2745                     removeLockCredential();
2746                     // Off/on to refresh the keyguard state.
2747                     pressSleepButton();
2748                     pressWakeupButton();
2749                     pressUnlockButton();
2750                 }
2751                 final String overlayDisplaySettings = Settings.Global.getString(
2752                         mContext.getContentResolver(), Settings.Global.OVERLAY_DISPLAY_DEVICES);
2753                 if (overlayDisplaySettings != null && overlayDisplaySettings.length() > 0) {
2754                     mLastError.addSuppressed(new IllegalStateException(
2755                             "Overlay display is found: " + overlayDisplaySettings));
2756                     // Remove the overlay display because it may obscure the screen and causes the
2757                     // next tests to fail.
2758                     SettingsSession.delete(Settings.Global.getUriFor(
2759                             Settings.Global.OVERLAY_DISPLAY_DEVICES));
2760                 }
2761             }
2762             if (!sIllegalTaskStateFound) {
2763                 // Skip if a illegal task state was already found in previous test, or all tests
2764                 // afterward could also fail and fire unnecessary false alarms.
2765                 try {
2766                     mWmState.assertIllegalTaskState();
2767                 } catch (Throwable t) {
2768                     sIllegalTaskStateFound = true;
2769                     addError(t);
2770                 }
2771             }
2772             super.verify();
2773         }
2774 
2775         @Override
2776         public void addError(Throwable error) {
2777             super.addError(error);
2778             logE("addError: " + error);
2779             mLastError = error;
2780         }
2781     }
2782 
2783     /** Activity that can handle all config changes. */
2784     public static class ConfigChangeHandlingActivity extends CommandSession.BasicTestActivity {
2785     }
2786 
2787     public static class ReportedDisplayMetrics {
2788         private static final String WM_SIZE = "wm size";
2789         private static final String WM_DENSITY = "wm density";
2790         private static final Pattern PHYSICAL_SIZE =
2791                 Pattern.compile("Physical size: (\\d+)x(\\d+)");
2792         private static final Pattern OVERRIDE_SIZE =
2793                 Pattern.compile("Override size: (\\d+)x(\\d+)");
2794         private static final Pattern PHYSICAL_DENSITY =
2795                 Pattern.compile("Physical density: (\\d+)");
2796         private static final Pattern OVERRIDE_DENSITY =
2797                 Pattern.compile("Override density: (\\d+)");
2798 
2799         /** The size of the physical display. */
2800         @NonNull
2801         final Size physicalSize;
2802         /** The density of the physical display. */
2803         final int physicalDensity;
2804 
2805         /** The pre-existing size override applied to a logical display. */
2806         @Nullable
2807         final Size overrideSize;
2808         /** The pre-existing density override applied to a logical display. */
2809         @Nullable
2810         final Integer overrideDensity;
2811 
2812         final int mDisplayId;
2813 
2814         /** Get physical and override display metrics from WM for specified display. */
2815         public static ReportedDisplayMetrics getDisplayMetrics(int displayId) {
2816             return new ReportedDisplayMetrics(executeShellCommand(WM_SIZE + " -d " + displayId)
2817                     + executeShellCommand(WM_DENSITY + " -d " + displayId), displayId);
2818         }
2819 
2820         public void setDisplayMetrics(final Size size, final int density) {
2821             setSize(size);
2822             setDensity(density);
2823         }
2824 
2825         public void restoreDisplayMetrics() {
2826             if (overrideSize != null) {
2827                 setSize(overrideSize);
2828             } else {
2829                 executeShellCommand(WM_SIZE + " reset -d " + mDisplayId);
2830             }
2831             if (overrideDensity != null) {
2832                 setDensity(overrideDensity);
2833             } else {
2834                 executeShellCommand(WM_DENSITY + " reset -d " + mDisplayId);
2835             }
2836         }
2837 
2838         public void setSize(final Size size) {
2839             executeShellCommand(
2840                     WM_SIZE + " " + size.getWidth() + "x" + size.getHeight() + " -d " + mDisplayId);
2841         }
2842 
2843         public void setDensity(final int density) {
2844             executeShellCommand(WM_DENSITY + " " + density + " -d " + mDisplayId);
2845         }
2846 
2847         /** Get display size that WM operates with. */
2848         public Size getSize() {
2849             return overrideSize != null ? overrideSize : physicalSize;
2850         }
2851 
2852         /** Get density that WM operates with. */
2853         public int getDensity() {
2854             return overrideDensity != null ? overrideDensity : physicalDensity;
2855         }
2856 
2857         private ReportedDisplayMetrics(final String lines, int displayId) {
2858             mDisplayId = displayId;
2859             Matcher matcher = PHYSICAL_SIZE.matcher(lines);
2860             assertTrue("Physical display size must be reported", matcher.find());
2861             log(matcher.group());
2862             physicalSize = new Size(
2863                     Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
2864 
2865             matcher = PHYSICAL_DENSITY.matcher(lines);
2866             assertTrue("Physical display density must be reported", matcher.find());
2867             log(matcher.group());
2868             physicalDensity = Integer.parseInt(matcher.group(1));
2869 
2870             matcher = OVERRIDE_SIZE.matcher(lines);
2871             if (matcher.find()) {
2872                 log(matcher.group());
2873                 overrideSize = new Size(
2874                         Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
2875             } else {
2876                 overrideSize = null;
2877             }
2878 
2879             matcher = OVERRIDE_DENSITY.matcher(lines);
2880             if (matcher.find()) {
2881                 log(matcher.group());
2882                 overrideDensity = Integer.parseInt(matcher.group(1));
2883             } else {
2884                 overrideDensity = null;
2885             }
2886         }
2887     }
2888 }
2889