Home | History | Annotate | Download | only in wm
      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.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
     20 import static android.app.Instrumentation.ActivityMonitor;
     21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
     22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
     23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
     24 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
     25 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
     26 import static android.content.Intent.ACTION_MAIN;
     27 import static android.content.Intent.CATEGORY_HOME;
     28 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
     29 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
     30 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
     31 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
     32 import static android.content.pm.PackageManager.DONT_KILL_APP;
     33 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
     34 import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
     35 import static android.content.pm.PackageManager.FEATURE_EMBEDDED;
     36 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
     37 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
     38 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
     39 import static android.content.pm.PackageManager.FEATURE_SCREEN_LANDSCAPE;
     40 import static android.content.pm.PackageManager.FEATURE_SCREEN_PORTRAIT;
     41 import static android.content.pm.PackageManager.FEATURE_SECURE_LOCK_SCREEN;
     42 import static android.content.pm.PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE;
     43 import static android.content.pm.PackageManager.FEATURE_WATCH;
     44 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
     45 import static android.server.wm.ActivityLauncher.KEY_ACTIVITY_TYPE;
     46 import static android.server.wm.ActivityLauncher.KEY_DISPLAY_ID;
     47 import static android.server.wm.ActivityLauncher.KEY_INTENT_EXTRAS;
     48 import static android.server.wm.ActivityLauncher.KEY_INTENT_FLAGS;
     49 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
     50 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_TO_SIDE;
     51 import static android.server.wm.ActivityLauncher.KEY_MULTIPLE_INSTANCES;
     52 import static android.server.wm.ActivityLauncher.KEY_MULTIPLE_TASK;
     53 import static android.server.wm.ActivityLauncher.KEY_NEW_TASK;
     54 import static android.server.wm.ActivityLauncher.KEY_RANDOM_DATA;
     55 import static android.server.wm.ActivityLauncher.KEY_REORDER_TO_FRONT;
     56 import static android.server.wm.ActivityLauncher.KEY_SUPPRESS_EXCEPTIONS;
     57 import static android.server.wm.ActivityLauncher.KEY_TARGET_COMPONENT;
     58 import static android.server.wm.ActivityLauncher.KEY_USE_APPLICATION_CONTEXT;
     59 import static android.server.wm.ActivityLauncher.KEY_USE_INSTRUMENTATION;
     60 import static android.server.wm.ActivityLauncher.launchActivityFromExtras;
     61 import static android.server.wm.ActivityManagerState.STATE_RESUMED;
     62 import static android.server.wm.ComponentNameUtils.getActivityName;
     63 import static android.server.wm.ComponentNameUtils.getLogTag;
     64 import static android.server.wm.StateLogger.log;
     65 import static android.server.wm.StateLogger.logAlways;
     66 import static android.server.wm.StateLogger.logE;
     67 import static android.server.wm.UiDeviceUtils.pressAppSwitchButton;
     68 import static android.server.wm.UiDeviceUtils.pressBackButton;
     69 import static android.server.wm.UiDeviceUtils.pressEnterButton;
     70 import static android.server.wm.UiDeviceUtils.pressHomeButton;
     71 import static android.server.wm.UiDeviceUtils.pressSleepButton;
     72 import static android.server.wm.UiDeviceUtils.pressUnlockButton;
     73 import static android.server.wm.UiDeviceUtils.pressWakeupButton;
     74 import static android.server.wm.UiDeviceUtils.waitForDeviceIdle;
     75 import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
     76 import static android.server.wm.app.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
     77 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_BROADCAST_ORIENTATION;
     78 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_CUTOUT_EXISTS;
     79 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD;
     80 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD_METHOD;
     81 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
     82 import static android.server.wm.app.Components.BroadcastReceiverActivity.EXTRA_MOVE_BROADCAST_TO_BACK;
     83 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
     84 import static android.server.wm.app.Components.PipActivity.ACTION_EXPAND_PIP;
     85 import static android.server.wm.app.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION;
     86 import static android.server.wm.app.Components.PipActivity.EXTRA_PIP_ORIENTATION;
     87 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR;
     88 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR;
     89 import static android.server.wm.app.Components.TEST_ACTIVITY;
     90 import static android.server.wm.second.Components.SECOND_ACTIVITY;
     91 import static android.server.wm.third.Components.THIRD_ACTIVITY;
     92 import static android.view.Display.DEFAULT_DISPLAY;
     93 import static android.view.Display.INVALID_DISPLAY;
     94 import static android.view.Surface.ROTATION_0;
     95 
     96 import static androidx.test.InstrumentationRegistry.getInstrumentation;
     97 
     98 import static org.junit.Assert.assertEquals;
     99 import static org.junit.Assert.assertNotNull;
    100 import static org.junit.Assert.assertTrue;
    101 import static org.junit.Assert.fail;
    102 
    103 import static java.lang.Integer.toHexString;
    104 
    105 import android.accessibilityservice.AccessibilityService;
    106 import android.app.Activity;
    107 import android.app.ActivityManager;
    108 import android.app.ActivityOptions;
    109 import android.app.ActivityTaskManager;
    110 import android.content.ComponentName;
    111 import android.content.ContentResolver;
    112 import android.content.Context;
    113 import android.content.Intent;
    114 import android.content.pm.PackageManager;
    115 import android.content.pm.ResolveInfo;
    116 import android.content.res.Resources;
    117 import android.database.ContentObserver;
    118 import android.graphics.Bitmap;
    119 import android.graphics.Rect;
    120 import android.hardware.display.AmbientDisplayConfiguration;
    121 import android.hardware.display.DisplayManager;
    122 import android.os.Bundle;
    123 import android.os.Handler;
    124 import android.os.HandlerThread;
    125 import android.os.SystemClock;
    126 import android.os.UserHandle;
    127 import android.provider.Settings;
    128 import android.server.wm.CommandSession.ActivityCallback;
    129 import android.server.wm.CommandSession.ActivitySession;
    130 import android.server.wm.CommandSession.ConfigInfo;
    131 import android.server.wm.CommandSession.LaunchInjector;
    132 import android.server.wm.CommandSession.LaunchProxy;
    133 import android.server.wm.CommandSession.SizeInfo;
    134 import android.server.wm.TestJournalProvider.TestJournalContainer;
    135 import android.server.wm.settings.SettingsSession;
    136 import android.util.EventLog;
    137 import android.util.EventLog.Event;
    138 import android.view.Display;
    139 import android.view.InputDevice;
    140 import android.view.MotionEvent;
    141 
    142 import androidx.annotation.NonNull;
    143 import androidx.annotation.Nullable;
    144 import androidx.test.rule.ActivityTestRule;
    145 
    146 import com.android.compatibility.common.util.SystemUtil;
    147 
    148 import org.junit.After;
    149 import org.junit.Before;
    150 import org.junit.Rule;
    151 import org.junit.rules.TestRule;
    152 import org.junit.runner.Description;
    153 import org.junit.runners.model.Statement;
    154 
    155 import java.io.IOException;
    156 import java.util.ArrayList;
    157 import java.util.Arrays;
    158 import java.util.Collections;
    159 import java.util.HashMap;
    160 import java.util.HashSet;
    161 import java.util.Iterator;
    162 import java.util.List;
    163 import java.util.Map;
    164 import java.util.UUID;
    165 import java.util.concurrent.Callable;
    166 import java.util.concurrent.TimeUnit;
    167 import java.util.concurrent.atomic.AtomicBoolean;
    168 import java.util.function.BooleanSupplier;
    169 import java.util.function.Consumer;
    170 import java.util.regex.Matcher;
    171 import java.util.regex.Pattern;
    172 
    173 public abstract class ActivityManagerTestBase {
    174     private static final boolean PRETEND_DEVICE_SUPPORTS_PIP = false;
    175     private static final boolean PRETEND_DEVICE_SUPPORTS_FREEFORM = false;
    176     private static final String LOG_SEPARATOR = "LOG_SEPARATOR";
    177     // Use one of the test tags as a separator
    178     private static final int EVENT_LOG_SEPARATOR_TAG = 42;
    179 
    180     protected static final int[] ALL_ACTIVITY_TYPE_BUT_HOME = {
    181             ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS,
    182             ACTIVITY_TYPE_UNDEFINED
    183     };
    184 
    185     private static final String TEST_PACKAGE = TEST_ACTIVITY.getPackageName();
    186     private static final String SECOND_TEST_PACKAGE = SECOND_ACTIVITY.getPackageName();
    187     private static final String THIRD_TEST_PACKAGE = THIRD_ACTIVITY.getPackageName();
    188     private static final List<String> TEST_PACKAGES;
    189 
    190     static {
    191         final List<String> testPackages = new ArrayList<>(3);
    192         testPackages.add(TEST_PACKAGE);
    193         testPackages.add(SECOND_TEST_PACKAGE);
    194         testPackages.add(THIRD_TEST_PACKAGE);
    195         testPackages.add("android.server.wm.cts");
    196         TEST_PACKAGES = Collections.unmodifiableList(testPackages);
    197     }
    198 
    199     protected static final String AM_START_HOME_ACTIVITY_COMMAND =
    200             "am start -a android.intent.action.MAIN -c android.intent.category.HOME";
    201 
    202     private static final String LOCK_CREDENTIAL = "1234";
    203 
    204     private static final int UI_MODE_TYPE_MASK = 0x0f;
    205     private static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
    206 
    207     private static Boolean sHasHomeScreen = null;
    208     private static Boolean sSupportsSystemDecorsOnSecondaryDisplays = null;
    209     private static Boolean sSupportsInsecureLockScreen = null;
    210 
    211     protected static final int INVALID_DEVICE_ROTATION = -1;
    212 
    213     protected Context mContext;
    214     protected ActivityManager mAm;
    215     protected ActivityTaskManager mAtm;
    216 
    217     /**
    218      * Callable to clear launch params for all test packages.
    219      */
    220     private final Callable<Void> mClearLaunchParamsCallable = () -> {
    221         mAtm.clearLaunchParamsForPackages(TEST_PACKAGES);
    222         return null;
    223     };
    224 
    225     @Rule
    226     public final ActivityTestRule<SideActivity> mSideActivityRule =
    227             new ActivityTestRule<>(SideActivity.class, true /* initialTouchMode */,
    228                     false /* launchActivity */);
    229 
    230     /**
    231      * @return the am command to start the given activity with the following extra key/value pairs.
    232      * {@param keyValuePairs} must be a list of arguments defining each key/value extra.
    233      */
    234     // TODO: Make this more generic, for instance accepting flags or extras of other types.
    235     protected static String getAmStartCmd(final ComponentName activityName,
    236             final String... keyValuePairs) {
    237         return getAmStartCmdInternal(getActivityName(activityName), keyValuePairs);
    238     }
    239 
    240     private static String getAmStartCmdInternal(final String activityName,
    241             final String... keyValuePairs) {
    242         return appendKeyValuePairs(
    243                 new StringBuilder("am start -n ").append(activityName),
    244                 keyValuePairs);
    245     }
    246 
    247     private static String appendKeyValuePairs(
    248             final StringBuilder cmd, final String... keyValuePairs) {
    249         if (keyValuePairs.length % 2 != 0) {
    250             throw new RuntimeException("keyValuePairs must be pairs of key/value arguments");
    251         }
    252         for (int i = 0; i < keyValuePairs.length; i += 2) {
    253             final String key = keyValuePairs[i];
    254             final String value = keyValuePairs[i + 1];
    255             cmd.append(" --es ")
    256                     .append(key)
    257                     .append(" ")
    258                     .append(value);
    259         }
    260         return cmd.toString();
    261     }
    262 
    263     protected static String getAmStartCmd(final ComponentName activityName, final int displayId,
    264             final String... keyValuePair) {
    265         return getAmStartCmdInternal(getActivityName(activityName), displayId, keyValuePair);
    266     }
    267 
    268     private static String getAmStartCmdInternal(final String activityName, final int displayId,
    269             final String... keyValuePairs) {
    270         return appendKeyValuePairs(
    271                 new StringBuilder("am start -n ")
    272                         .append(activityName)
    273                         .append(" -f 0x")
    274                         .append(toHexString(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK))
    275                         .append(" --display ")
    276                         .append(displayId),
    277                 keyValuePairs);
    278     }
    279 
    280     protected static String getAmStartCmdInNewTask(final ComponentName activityName) {
    281         return "am start -n " + getActivityName(activityName) + " -f 0x18000000";
    282     }
    283 
    284     protected static String getAmStartCmdOverHome(final ComponentName activityName) {
    285         return "am start --activity-task-on-home -n " + getActivityName(activityName);
    286     }
    287 
    288     protected ActivityAndWindowManagersState mAmWmState = new ActivityAndWindowManagersState();
    289 
    290     public ActivityAndWindowManagersState getAmWmState() {
    291         return mAmWmState;
    292     }
    293 
    294     protected BroadcastActionTrigger mBroadcastActionTrigger = new BroadcastActionTrigger();
    295 
    296     /**
    297      * Helper class to process test actions by broadcast.
    298      */
    299     protected class BroadcastActionTrigger {
    300 
    301         private Intent createIntentWithAction(String broadcastAction) {
    302             return new Intent(broadcastAction)
    303                     .setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    304         }
    305 
    306         void doAction(String broadcastAction) {
    307             mContext.sendBroadcast(createIntentWithAction(broadcastAction));
    308         }
    309 
    310         void finishBroadcastReceiverActivity() {
    311             mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
    312                     .putExtra(EXTRA_FINISH_BROADCAST, true));
    313         }
    314 
    315         void launchActivityNewTask(String launchComponent) {
    316             mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
    317                     .putExtra(KEY_LAUNCH_ACTIVITY, true)
    318                     .putExtra(KEY_NEW_TASK, true)
    319                     .putExtra(KEY_TARGET_COMPONENT, launchComponent));
    320         }
    321 
    322         void moveTopTaskToBack() {
    323             mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
    324                     .putExtra(EXTRA_MOVE_BROADCAST_TO_BACK, true));
    325         }
    326 
    327         void requestOrientation(int orientation) {
    328             mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
    329                     .putExtra(EXTRA_BROADCAST_ORIENTATION, orientation));
    330         }
    331 
    332         void dismissKeyguardByFlag() {
    333             mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
    334                     .putExtra(EXTRA_DISMISS_KEYGUARD, true));
    335         }
    336 
    337         void dismissKeyguardByMethod() {
    338             mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
    339                     .putExtra(EXTRA_DISMISS_KEYGUARD_METHOD, true));
    340         }
    341 
    342         void expandPipWithAspectRatio(String extraNum, String extraDenom) {
    343             mContext.sendBroadcast(createIntentWithAction(ACTION_EXPAND_PIP)
    344                     .putExtra(EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR, extraNum)
    345                     .putExtra(EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR, extraDenom));
    346         }
    347 
    348         void requestOrientationForPip(int orientation) {
    349             mContext.sendBroadcast(createIntentWithAction(ACTION_SET_REQUESTED_ORIENTATION)
    350                     .putExtra(EXTRA_PIP_ORIENTATION, String.valueOf(orientation)));
    351         }
    352     }
    353 
    354     /**
    355      * Helper class to launch / close test activity by instrumentation way.
    356      */
    357     protected class TestActivitySession<T extends Activity> implements AutoCloseable {
    358         private T mTestActivity;
    359         boolean mFinishAfterClose;
    360         private static final int ACTIVITY_LAUNCH_TIMEOUT = 10000;
    361         private static final int WAIT_SLICE = 50;
    362 
    363         void launchTestActivityOnDisplaySync(Class<T> activityClass, int displayId) {
    364             launchTestActivityOnDisplaySync(new Intent(mContext, activityClass), displayId);
    365         }
    366 
    367         void launchTestActivityOnDisplaySync(Intent intent, int displayId) {
    368             SystemUtil.runWithShellPermissionIdentity(() -> {
    369                 final Bundle bundle = ActivityOptions.makeBasic()
    370                         .setLaunchDisplayId(displayId).toBundle();
    371                 final ActivityMonitor monitor = getInstrumentation()
    372                         .addMonitor((String) null, null, false);
    373                 mContext.startActivity(intent.addFlags(FLAG_ACTIVITY_NEW_TASK), bundle);
    374                 // Wait for activity launch with timeout.
    375                 mTestActivity = (T) monitor.waitForActivityWithTimeout(ACTIVITY_LAUNCH_TIMEOUT);
    376                 assertNotNull(mTestActivity);
    377                 // Check activity is launched and resumed.
    378                 final ComponentName testActivityName = mTestActivity.getComponentName();
    379                 waitAndAssertTopResumedActivity(testActivityName, displayId,
    380                         "Activity must be resumed");
    381             });
    382         }
    383 
    384         void finishCurrentActivityNoWait() {
    385             if (mTestActivity != null) {
    386                 mTestActivity.finishAndRemoveTask();
    387                 mTestActivity = null;
    388             }
    389         }
    390 
    391         void runOnMainSyncAndWait(Runnable runnable) {
    392             getInstrumentation().runOnMainSync(runnable);
    393             getInstrumentation().waitForIdleSync();
    394         }
    395 
    396         void runOnMainAndAssertWithTimeout(@NonNull BooleanSupplier condition, long timeoutMs,
    397                 String message) {
    398             final AtomicBoolean result = new AtomicBoolean();
    399             final long expiredTime = System.currentTimeMillis() + timeoutMs;
    400             while (!result.get()) {
    401                 if (System.currentTimeMillis() >= expiredTime) {
    402                     fail(message);
    403                 }
    404                 runOnMainSyncAndWait(() -> {
    405                     if (condition.getAsBoolean()) {
    406                         result.set(true);
    407                     }
    408                 });
    409                 SystemClock.sleep(WAIT_SLICE);
    410             }
    411         }
    412 
    413         T getActivity() {
    414             return mTestActivity;
    415         }
    416 
    417         @Override
    418         public void close() throws Exception {
    419             if (mTestActivity != null && mFinishAfterClose) {
    420                 mTestActivity.finishAndRemoveTask();
    421             }
    422         }
    423     }
    424 
    425     @Before
    426     public void setUp() throws Exception {
    427         mContext = getInstrumentation().getContext();
    428         mAm = mContext.getSystemService(ActivityManager.class);
    429         mAtm = mContext.getSystemService(ActivityTaskManager.class);
    430 
    431         pressWakeupButton();
    432         pressUnlockButton();
    433         pressHomeButton();
    434         removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
    435 
    436         // Clear launch params for all test packages to make sure each test is run in a clean state.
    437         SystemUtil.callWithShellPermissionIdentity(mClearLaunchParamsCallable);
    438     }
    439 
    440     @After
    441     public void tearDown() throws Exception {
    442         // Synchronous execution of removeStacksWithActivityTypes() ensures that all activities but
    443         // home are cleaned up from the stack at the end of each test. Am force stop shell commands
    444         // might be asynchronous and could interrupt the stack cleanup process if executed first.
    445         removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
    446         stopTestPackage(TEST_PACKAGE);
    447         stopTestPackage(SECOND_TEST_PACKAGE);
    448         stopTestPackage(THIRD_TEST_PACKAGE);
    449         pressHomeButton();
    450 
    451     }
    452 
    453     protected void moveTopActivityToPinnedStack(int stackId) {
    454         SystemUtil.runWithShellPermissionIdentity(
    455                 () -> mAtm.moveTopActivityToPinnedStack(stackId, new Rect(0, 0, 500, 500))
    456         );
    457     }
    458 
    459     protected void startActivityOnDisplay(int displayId, ComponentName component) {
    460         final ActivityOptions options = ActivityOptions.makeBasic();
    461         options.setLaunchDisplayId(displayId);
    462 
    463         mContext.startActivity(new Intent().addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    464                 .setComponent(component), options.toBundle());
    465     }
    466 
    467     protected boolean noHomeScreen() {
    468         try {
    469             return mContext.getResources().getBoolean(
    470                     Resources.getSystem().getIdentifier("config_noHomeScreen", "bool",
    471                             "android"));
    472         } catch (Resources.NotFoundException e) {
    473             // Assume there's a home screen.
    474             return false;
    475         }
    476     }
    477 
    478     private boolean getSupportsSystemDecorsOnSecondaryDisplays() {
    479         try {
    480             return mContext.getResources().getBoolean(
    481                     Resources.getSystem().getIdentifier(
    482                             "config_supportsSystemDecorsOnSecondaryDisplays", "bool", "android"));
    483         } catch (Resources.NotFoundException e) {
    484             // Assume this device support system decorations.
    485             return true;
    486         }
    487     }
    488 
    489     protected ComponentName getDefaultSecondaryHomeComponent() {
    490         int resId = Resources.getSystem().getIdentifier(
    491                 "config_secondaryHomeComponent", "string", "android");
    492         return ComponentName.unflattenFromString(mContext.getResources().getString(resId));
    493     }
    494 
    495     protected void tapOnDisplay(int x, int y, int displayId) {
    496         final long downTime = SystemClock.uptimeMillis();
    497         injectMotion(downTime, downTime, MotionEvent.ACTION_DOWN, x, y, displayId);
    498 
    499         final long upTime = SystemClock.uptimeMillis();
    500         injectMotion(downTime, upTime, MotionEvent.ACTION_UP, x, y, displayId);
    501     }
    502 
    503     protected void tapOnStackCenter(ActivityManagerState.ActivityStack stack) {
    504         final Rect sideStackBounds = stack.getBounds();
    505         final int tapX = sideStackBounds.left + sideStackBounds.width() / 2;
    506         final int tapY = sideStackBounds.top + sideStackBounds.height() / 2;
    507         tapOnDisplay(tapX, tapY, stack.mDisplayId);
    508     }
    509 
    510     private static void injectMotion(long downTime, long eventTime, int action,
    511             int x, int y, int displayId) {
    512         final MotionEvent event = MotionEvent.obtain(downTime, eventTime, action,
    513                 x, y, 0 /* metaState */);
    514         event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
    515         event.setDisplayId(displayId);
    516         getInstrumentation().getUiAutomation().injectInputEvent(event, true /* sync */);
    517     }
    518 
    519     protected void removeStacksWithActivityTypes(int... activityTypes) {
    520         SystemUtil.runWithShellPermissionIdentity(
    521                 () -> mAtm.removeStacksWithActivityTypes(activityTypes));
    522         waitForIdle();
    523     }
    524 
    525     protected void removeStacksInWindowingModes(int... windowingModes) {
    526         SystemUtil.runWithShellPermissionIdentity(
    527                 () -> mAtm.removeStacksInWindowingModes(windowingModes)
    528         );
    529         waitForIdle();
    530     }
    531 
    532     public static String executeShellCommand(String command) {
    533         log("Shell command: " + command);
    534         try {
    535             return SystemUtil.runShellCommand(getInstrumentation(), command);
    536         } catch (IOException e) {
    537             //bubble it up
    538             logE("Error running shell command: " + command);
    539             throw new RuntimeException(e);
    540         }
    541     }
    542 
    543     protected Bitmap takeScreenshot() {
    544         return getInstrumentation().getUiAutomation().takeScreenshot();
    545     }
    546 
    547     protected void launchActivity(final ComponentName activityName, final String... keyValuePairs) {
    548         launchActivityNoWait(activityName, keyValuePairs);
    549         mAmWmState.waitForValidState(activityName);
    550     }
    551 
    552     protected void launchActivityNoWait(final ComponentName activityName,
    553             final String... keyValuePairs) {
    554         executeShellCommand(getAmStartCmd(activityName, keyValuePairs));
    555     }
    556 
    557     protected void launchActivityInNewTask(final ComponentName activityName) {
    558         executeShellCommand(getAmStartCmdInNewTask(activityName));
    559         mAmWmState.waitForValidState(activityName);
    560     }
    561 
    562     private static void waitForIdle() {
    563         getInstrumentation().waitForIdleSync();
    564     }
    565 
    566     /** Returns the set of stack ids. */
    567     private HashSet<Integer> getStackIds() {
    568         mAmWmState.computeState(true);
    569         final List<ActivityManagerState.ActivityStack> stacks = mAmWmState.getAmState().getStacks();
    570         final HashSet<Integer> stackIds = new HashSet<>();
    571         for (ActivityManagerState.ActivityStack s : stacks) {
    572             stackIds.add(s.mStackId);
    573         }
    574         return stackIds;
    575     }
    576 
    577     /** Returns the stack that contains the provided task. */
    578     protected ActivityManagerState.ActivityStack getStackForTaskId(int taskId) {
    579         mAmWmState.computeState(true);
    580         final List<ActivityManagerState.ActivityStack> stacks = mAmWmState.getAmState().getStacks();
    581         for (ActivityManagerState.ActivityStack stack : stacks) {
    582             for (ActivityManagerState.ActivityTask task : stack.mTasks) {
    583                 if (task.mTaskId == taskId) {
    584                     return stack;
    585                 }
    586             }
    587         }
    588         return null;
    589     }
    590 
    591     protected void launchHomeActivity() {
    592         executeShellCommand(AM_START_HOME_ACTIVITY_COMMAND);
    593         mAmWmState.waitForHomeActivityVisible();
    594     }
    595 
    596     protected void launchActivity(ComponentName activityName, int windowingMode,
    597             final String... keyValuePairs) {
    598         executeShellCommand(getAmStartCmd(activityName, keyValuePairs)
    599                 + " --windowingMode " + windowingMode);
    600         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
    601                 .setWindowingMode(windowingMode)
    602                 .build());
    603     }
    604 
    605     protected void launchActivityOnDisplay(ComponentName activityName, int windowingMode,
    606             int displayId, final String... keyValuePairs) {
    607         executeShellCommand(getAmStartCmd(activityName, displayId, keyValuePairs)
    608                 + " --windowingMode " + windowingMode);
    609         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
    610                 .setWindowingMode(windowingMode)
    611                 .build());
    612     }
    613 
    614     protected void launchActivityOnDisplay(ComponentName activityName, int displayId,
    615             String... keyValuePairs) {
    616         launchActivityOnDisplayNoWait(activityName, displayId, keyValuePairs);
    617         mAmWmState.waitForValidState(activityName);
    618     }
    619 
    620     protected void launchActivityOnDisplayNoWait(ComponentName activityName, int displayId,
    621             String... keyValuePairs) {
    622         executeShellCommand(getAmStartCmd(activityName, displayId, keyValuePairs));
    623     }
    624 
    625     /**
    626      * Launches {@param activityName} into split-screen primary windowing mode and also makes
    627      * the recents activity visible to the side of it.
    628      * NOTE: Recents view may be combined with home screen on some devices, so using this to wait
    629      * for Recents only makes sense when {@link ActivityManagerState#isHomeRecentsComponent()} is
    630      * {@code false}.
    631      */
    632     protected void launchActivityInSplitScreenWithRecents(ComponentName activityName) {
    633         launchActivityInSplitScreenWithRecents(activityName, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
    634     }
    635 
    636     protected void launchActivityInSplitScreenWithRecents(ComponentName activityName,
    637             int createMode) {
    638         SystemUtil.runWithShellPermissionIdentity(() -> {
    639             launchActivity(activityName);
    640             final int taskId = mAmWmState.getAmState().getTaskByActivity(activityName).mTaskId;
    641             mAtm.setTaskWindowingModeSplitScreenPrimary(taskId, createMode,
    642                     true /* onTop */, false /* animate */,
    643                     null /* initialBounds */, true /* showRecents */);
    644 
    645             mAmWmState.waitForValidState(
    646                     new WaitForValidActivityState.Builder(activityName)
    647                             .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
    648                             .setActivityType(ACTIVITY_TYPE_STANDARD)
    649                             .build());
    650             mAmWmState.waitForRecentsActivityVisible();
    651         });
    652     }
    653 
    654     public void moveTaskToPrimarySplitScreen(int taskId) {
    655         moveTaskToPrimarySplitScreen(taskId, false /* showRecents */);
    656     }
    657 
    658     /**
    659      * Moves the device into split-screen with the specified task into the primary stack.
    660      * @param taskId             The id of the task to move into the primary stack.
    661      * @param showSideActivity   Whether to show the Recents activity (or a placeholder activity in
    662      *                           place of the Recents activity if home is the recents component)
    663      */
    664     public void moveTaskToPrimarySplitScreen(int taskId, boolean showSideActivity) {
    665         final boolean isHomeRecentsComponent = mAmWmState.getAmState().isHomeRecentsComponent();
    666         SystemUtil.runWithShellPermissionIdentity(() -> {
    667             mAtm.setTaskWindowingModeSplitScreenPrimary(taskId,
    668                     SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true /* onTop */,
    669                     false /* animate */,
    670                     null /* initialBounds */, showSideActivity && !isHomeRecentsComponent);
    671             mAmWmState.waitForRecentsActivityVisible();
    672 
    673             if (isHomeRecentsComponent && showSideActivity) {
    674                 // Launch Placeholder Side Activity
    675                 final Activity sideActivity = mSideActivityRule.launchActivity(
    676                         new Intent());
    677                 mAmWmState.waitForActivityState(sideActivity.getComponentName(), STATE_RESUMED);
    678             }
    679         });
    680     }
    681 
    682     /**
    683      * Launches {@param primaryActivity} into split-screen primary windowing mode
    684      * and {@param secondaryActivity} to the side in split-screen secondary windowing mode.
    685      */
    686     protected void launchActivitiesInSplitScreen(LaunchActivityBuilder primaryActivity,
    687             LaunchActivityBuilder secondaryActivity) {
    688         // Launch split-screen primary.
    689         primaryActivity
    690                 .setUseInstrumentation()
    691                 .setWaitForLaunched(true)
    692                 .execute();
    693 
    694         final int taskId = mAmWmState.getAmState().getTaskByActivity(
    695                 primaryActivity.mTargetActivity).mTaskId;
    696         moveTaskToPrimarySplitScreen(taskId);
    697 
    698         // Launch split-screen secondary
    699         // Recents become focused, so we can just launch new task in focused stack
    700         secondaryActivity
    701                 .setUseInstrumentation()
    702                 .setWaitForLaunched(true)
    703                 .setNewTask(true)
    704                 .setMultipleTask(true)
    705                 .execute();
    706     }
    707 
    708     protected void setActivityTaskWindowingMode(ComponentName activityName, int windowingMode) {
    709         mAmWmState.computeState(activityName);
    710         final int taskId = mAmWmState.getAmState().getTaskByActivity(activityName).mTaskId;
    711         SystemUtil.runWithShellPermissionIdentity(
    712                 () -> mAtm.setTaskWindowingMode(taskId, windowingMode, true /* toTop */));
    713         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
    714                 .setActivityType(ACTIVITY_TYPE_STANDARD)
    715                 .setWindowingMode(windowingMode)
    716                 .build());
    717     }
    718 
    719     protected void moveActivityToStack(ComponentName activityName, int stackId) {
    720         mAmWmState.computeState(activityName);
    721         final int taskId = mAmWmState.getAmState().getTaskByActivity(activityName).mTaskId;
    722         SystemUtil.runWithShellPermissionIdentity(
    723                 () -> mAtm.moveTaskToStack(taskId, stackId, true));
    724 
    725         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
    726                 .setStackId(stackId)
    727                 .build());
    728     }
    729 
    730     protected void resizeActivityTask(
    731             ComponentName activityName, int left, int top, int right, int bottom) {
    732         mAmWmState.computeState(activityName);
    733         final int taskId = mAmWmState.getAmState().getTaskByActivity(activityName).mTaskId;
    734         SystemUtil.runWithShellPermissionIdentity(
    735                 () -> mAtm.resizeTask(taskId, new Rect(left, top, right, bottom)));
    736     }
    737 
    738     protected void resizeDockedStack(
    739             int stackWidth, int stackHeight, int taskWidth, int taskHeight) {
    740         SystemUtil.runWithShellPermissionIdentity(() ->
    741                 mAtm.resizeDockedStack(new Rect(0, 0, stackWidth, stackHeight),
    742                         new Rect(0, 0, taskWidth, taskHeight)));
    743     }
    744 
    745     protected void resizeStack(int stackId, int stackLeft, int stackTop, int stackWidth,
    746             int stackHeight) {
    747         SystemUtil.runWithShellPermissionIdentity(() -> mAtm.resizeStack(stackId,
    748                 new Rect(stackLeft, stackTop, stackWidth, stackHeight)));
    749     }
    750 
    751     protected void pressAppSwitchButtonAndWaitForRecents() {
    752         pressAppSwitchButton();
    753         mAmWmState.waitForRecentsActivityVisible();
    754         mAmWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
    755     }
    756 
    757     // Utility method for debugging, not used directly here, but useful, so kept around.
    758     protected void printStacksAndTasks() {
    759         SystemUtil.runWithShellPermissionIdentity(() -> {
    760             final String output = mAtm.listAllStacks();
    761             for (String line : output.split("\\n")) {
    762                 log(line);
    763             }
    764         });
    765     }
    766 
    767     protected boolean supportsVrMode() {
    768         return hasDeviceFeature(FEATURE_VR_MODE_HIGH_PERFORMANCE);
    769     }
    770 
    771     protected boolean supportsPip() {
    772         return hasDeviceFeature(FEATURE_PICTURE_IN_PICTURE)
    773                 || PRETEND_DEVICE_SUPPORTS_PIP;
    774     }
    775 
    776     protected boolean supportsFreeform() {
    777         return hasDeviceFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
    778                 || PRETEND_DEVICE_SUPPORTS_FREEFORM;
    779     }
    780 
    781     /** Whether or not the device supports pin/pattern/password lock. */
    782     protected boolean supportsSecureLock() {
    783         return hasDeviceFeature(FEATURE_SECURE_LOCK_SCREEN);
    784     }
    785 
    786     /** Whether or not the device supports "swipe" lock. */
    787     protected boolean supportsInsecureLock() {
    788         return !hasDeviceFeature(FEATURE_LEANBACK)
    789                 && !hasDeviceFeature(FEATURE_WATCH)
    790                 && !hasDeviceFeature(FEATURE_EMBEDDED)
    791                 && !hasDeviceFeature(FEATURE_AUTOMOTIVE)
    792                 && getSupportsInsecureLockScreen();
    793     }
    794 
    795     protected boolean isWatch() {
    796         return hasDeviceFeature(FEATURE_WATCH);
    797     }
    798 
    799     protected boolean isTablet() {
    800         // Larger than approx 7" tablets
    801         return mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
    802     }
    803 
    804     protected void waitAndAssertActivityState(ComponentName activityName,
    805             String state, String message) {
    806         mAmWmState.waitForActivityState(activityName, state);
    807 
    808         assertTrue(message, mAmWmState.getAmState().hasActivityState(activityName, state));
    809     }
    810 
    811     public void waitAndAssertTopResumedActivity(ComponentName activityName, int displayId,
    812             String message) {
    813         mAmWmState.waitForValidState(activityName);
    814         mAmWmState.waitForActivityState(activityName, STATE_RESUMED);
    815         final String activityClassName = getActivityName(activityName);
    816         mAmWmState.waitForWithAmState(state ->
    817                         activityClassName.equals(state.getFocusedActivity()),
    818                 "Waiting for activity to be on top");
    819 
    820         mAmWmState.assertSanity();
    821         mAmWmState.assertFocusedActivity(message, activityName);
    822         assertTrue("Activity must be resumed",
    823                 mAmWmState.getAmState().hasActivityState(activityName, STATE_RESUMED));
    824         final int frontStackId = mAmWmState.getAmState().getFrontStackId(displayId);
    825         ActivityManagerState.ActivityStack frontStackOnDisplay =
    826                 mAmWmState.getAmState().getStackById(frontStackId);
    827         assertEquals("Resumed activity of front stack of the target display must match. " + message,
    828                 activityClassName, frontStackOnDisplay.mResumedActivity);
    829         mAmWmState.assertFocusedStack("Top activity's stack must also be on top", frontStackId);
    830         mAmWmState.assertVisibility(activityName, true /* visible */);
    831     }
    832 
    833     // TODO: Switch to using a feature flag, when available.
    834     protected static boolean isUiModeLockedToVrHeadset() {
    835         final String output = runCommandAndPrintOutput("dumpsys uimode");
    836 
    837         Integer curUiMode = null;
    838         Boolean uiModeLocked = null;
    839         for (String line : output.split("\\n")) {
    840             line = line.trim();
    841             Matcher matcher = sCurrentUiModePattern.matcher(line);
    842             if (matcher.find()) {
    843                 curUiMode = Integer.parseInt(matcher.group(1), 16);
    844             }
    845             matcher = sUiModeLockedPattern.matcher(line);
    846             if (matcher.find()) {
    847                 uiModeLocked = matcher.group(1).equals("true");
    848             }
    849         }
    850 
    851         boolean uiModeLockedToVrHeadset = (curUiMode != null) && (uiModeLocked != null)
    852                 && ((curUiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET) && uiModeLocked;
    853 
    854         if (uiModeLockedToVrHeadset) {
    855             log("UI mode is locked to VR headset");
    856         }
    857 
    858         return uiModeLockedToVrHeadset;
    859     }
    860 
    861     protected boolean supportsSplitScreenMultiWindow() {
    862         return ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
    863     }
    864 
    865     protected boolean hasHomeScreen() {
    866         if (sHasHomeScreen == null) {
    867             sHasHomeScreen = !noHomeScreen();
    868         }
    869         return sHasHomeScreen;
    870     }
    871 
    872     protected boolean supportsSystemDecorsOnSecondaryDisplays() {
    873         if (sSupportsSystemDecorsOnSecondaryDisplays == null) {
    874             sSupportsSystemDecorsOnSecondaryDisplays = getSupportsSystemDecorsOnSecondaryDisplays();
    875         }
    876         return sSupportsSystemDecorsOnSecondaryDisplays;
    877     }
    878 
    879     protected boolean getSupportsInsecureLockScreen() {
    880         if (sSupportsInsecureLockScreen == null) {
    881             try {
    882                 sSupportsInsecureLockScreen = mContext.getResources().getBoolean(
    883                         Resources.getSystem().getIdentifier(
    884                                 "config_supportsInsecureLockScreen", "bool", "android"));
    885             } catch (Resources.NotFoundException e) {
    886                 sSupportsInsecureLockScreen = true;
    887             }
    888         }
    889         return sSupportsInsecureLockScreen;
    890     }
    891 
    892     /**
    893      * Rotation support is indicated by explicitly having both landscape and portrait
    894      * features or not listing either at all.
    895      */
    896     protected boolean supportsRotation() {
    897         final boolean supportsLandscape = hasDeviceFeature(FEATURE_SCREEN_LANDSCAPE);
    898         final boolean supportsPortrait = hasDeviceFeature(FEATURE_SCREEN_PORTRAIT);
    899         return (supportsLandscape && supportsPortrait)
    900                 || (!supportsLandscape && !supportsPortrait);
    901     }
    902 
    903     protected boolean hasDeviceFeature(final String requiredFeature) {
    904         return mContext.getPackageManager()
    905                 .hasSystemFeature(requiredFeature);
    906     }
    907 
    908     protected static boolean isDisplayOn(int displayId) {
    909         final DisplayManager displayManager = getInstrumentation()
    910                 .getContext().getSystemService(DisplayManager.class);
    911         final Display display = displayManager.getDisplay(displayId);
    912         return display != null && display.getState() == Display.STATE_ON;
    913     }
    914 
    915     protected static boolean perDisplayFocusEnabled() {
    916         return getInstrumentation().getTargetContext().getResources()
    917                 .getBoolean(android.R.bool.config_perDisplayFocusEnabled);
    918     }
    919 
    920     /**
    921      * Test @Rule class that disables screen doze settings before each test method running and
    922      * restoring to initial values after test method finished.
    923      */
    924     protected static class DisableScreenDozeRule implements TestRule {
    925 
    926         /** Copied from android.provider.Settings.Secure since these keys are hiden. */
    927         private static final String[] DOZE_SETTINGS = {
    928                 "doze_enabled",
    929                 "doze_always_on",
    930                 "doze_pulse_on_pick_up",
    931                 "doze_pulse_on_long_press",
    932                 "doze_pulse_on_double_tap"
    933         };
    934 
    935         private String get(String key) {
    936             return executeShellCommand("settings get secure " + key).trim();
    937         }
    938 
    939         private void put(String key, String value) {
    940             executeShellCommand("settings put secure " + key + " " + value);
    941         }
    942 
    943         @Override
    944         public Statement apply(final Statement base, final Description description) {
    945             return new Statement() {
    946                 @Override
    947                 public void evaluate() throws Throwable {
    948                     final Map<String, String> initialValues = new HashMap<>();
    949                     Arrays.stream(DOZE_SETTINGS).forEach(k -> initialValues.put(k, get(k)));
    950                     try {
    951                         Arrays.stream(DOZE_SETTINGS).forEach(k -> put(k, "0"));
    952                         base.evaluate();
    953                     } finally {
    954                         Arrays.stream(DOZE_SETTINGS).forEach(k -> put(k, initialValues.get(k)));
    955                     }
    956                 }
    957             };
    958         }
    959     }
    960 
    961     /**
    962      * HomeActivitySession is used to replace the default home component, so that you can use
    963      * your preferred home for testing within the session. The original default home will be
    964      * restored automatically afterward.
    965      */
    966     protected class HomeActivitySession implements AutoCloseable {
    967         private PackageManager mPackageManager;
    968         private ComponentName mOrigHome;
    969         private ComponentName mSessionHome;
    970 
    971         public HomeActivitySession(ComponentName sessionHome) {
    972             mSessionHome = sessionHome;
    973             mPackageManager = mContext.getPackageManager();
    974 
    975             final Intent intent = new Intent(ACTION_MAIN);
    976             intent.addCategory(CATEGORY_HOME);
    977             intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
    978             final ResolveInfo resolveInfo =
    979                     mPackageManager.resolveActivity(intent, MATCH_DEFAULT_ONLY);
    980             if (resolveInfo != null) {
    981                 mOrigHome = new ComponentName(resolveInfo.activityInfo.packageName,
    982                         resolveInfo.activityInfo.name);
    983             }
    984 
    985             SystemUtil.runWithShellPermissionIdentity(
    986                     () -> mPackageManager.setComponentEnabledSetting(mSessionHome,
    987                             COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP));
    988             setDefaultHome(mSessionHome);
    989         }
    990 
    991         @Override
    992         public void close() {
    993             SystemUtil.runWithShellPermissionIdentity(
    994                     () -> mPackageManager.setComponentEnabledSetting(mSessionHome,
    995                             COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP));
    996             if (mOrigHome != null) {
    997                 setDefaultHome(mOrigHome);
    998             }
    999         }
   1000 
   1001         private void setDefaultHome(ComponentName componentName) {
   1002             executeShellCommand("cmd package set-home-activity --user "
   1003                     + android.os.Process.myUserHandle().getIdentifier() + " "
   1004                     + componentName.flattenToString());
   1005         }
   1006     }
   1007 
   1008     protected class LockScreenSession implements AutoCloseable {
   1009         private static final boolean DEBUG = false;
   1010 
   1011         private final boolean mIsLockDisabled;
   1012         private boolean mLockCredentialSet;
   1013         private boolean mRemoveActivitiesOnClose;
   1014         private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
   1015 
   1016         public static final int FLAG_REMOVE_ACTIVITIES_ON_CLOSE = 1;
   1017 
   1018         public LockScreenSession() {
   1019             this(0 /* flags */);
   1020         }
   1021 
   1022         public LockScreenSession(int flags) {
   1023             mIsLockDisabled = isLockDisabled();
   1024             mLockCredentialSet = false;
   1025             // Enable lock screen (swipe) by default.
   1026             setLockDisabled(false);
   1027             if ((flags & FLAG_REMOVE_ACTIVITIES_ON_CLOSE) != 0) {
   1028                 mRemoveActivitiesOnClose = true;
   1029             }
   1030             mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
   1031         }
   1032 
   1033         public LockScreenSession setLockCredential() {
   1034             mLockCredentialSet = true;
   1035             runCommandAndPrintOutput("locksettings set-pin " + LOCK_CREDENTIAL);
   1036             return this;
   1037         }
   1038 
   1039         public LockScreenSession enterAndConfirmLockCredential() {
   1040             // Ensure focus will switch to default display. Meanwhile we cannot tap on center area,
   1041             // which may tap on input credential area.
   1042             tapOnDisplay(10, 10, DEFAULT_DISPLAY);
   1043 
   1044             waitForDeviceIdle(3000);
   1045             SystemUtil.runWithShellPermissionIdentity(() ->
   1046                     getInstrumentation().sendStringSync(LOCK_CREDENTIAL));
   1047             pressEnterButton();
   1048             return this;
   1049         }
   1050 
   1051         private void removeLockCredential() {
   1052             runCommandAndPrintOutput("locksettings clear --old " + LOCK_CREDENTIAL);
   1053             mLockCredentialSet = false;
   1054         }
   1055 
   1056         LockScreenSession disableLockScreen() {
   1057             setLockDisabled(true);
   1058             return this;
   1059         }
   1060 
   1061         LockScreenSession sleepDevice() {
   1062             pressSleepButton();
   1063             // Not all device variants lock when we go to sleep, so we need to explicitly lock the
   1064             // device. Note that pressSleepButton() above is redundant because the action also
   1065             // puts the device to sleep, but kept around for clarity.
   1066             getInstrumentation().getUiAutomation().performGlobalAction(
   1067                     AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN);
   1068             if (mAmbientDisplayConfiguration.alwaysOnEnabled(
   1069                     android.os.Process.myUserHandle().getIdentifier())) {
   1070                 mAmWmState.waitForAodShowing();
   1071             } else {
   1072                 for (int retry = 1; isDisplayOn(DEFAULT_DISPLAY) && retry <= 5; retry++) {
   1073                     logAlways("***Waiting for display to turn off... retry=" + retry);
   1074                     SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
   1075                 }
   1076             }
   1077             return this;
   1078         }
   1079 
   1080         LockScreenSession wakeUpDevice() {
   1081             pressWakeupButton();
   1082             return this;
   1083         }
   1084 
   1085         LockScreenSession unlockDevice() {
   1086             pressUnlockButton();
   1087             return this;
   1088         }
   1089 
   1090         public LockScreenSession gotoKeyguard(ComponentName... showWhenLockedActivities) {
   1091             if (DEBUG && isLockDisabled()) {
   1092                 logE("LockScreenSession.gotoKeyguard() is called without lock enabled.");
   1093             }
   1094             sleepDevice();
   1095             wakeUpDevice();
   1096             if (showWhenLockedActivities.length == 0) {
   1097                 mAmWmState.waitForKeyguardShowingAndNotOccluded();
   1098             } else {
   1099                 mAmWmState.waitForValidState(showWhenLockedActivities);
   1100             }
   1101             return this;
   1102         }
   1103 
   1104         @Override
   1105         public void close() {
   1106             if (mRemoveActivitiesOnClose) {
   1107                 removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
   1108             }
   1109 
   1110             setLockDisabled(mIsLockDisabled);
   1111             if (mLockCredentialSet) {
   1112                 removeLockCredential();
   1113             }
   1114 
   1115             // Dismiss active keyguard after credential is cleared, so keyguard doesn't ask for
   1116             // the stale credential.
   1117             // TODO (b/112015010) If keyguard is occluded, credential cannot be removed as expected.
   1118             // LockScreenSession#close is always calls before stop all test activities,
   1119             // which could cause keyguard stay at occluded after wakeup.
   1120             // If Keyguard is occluded, press back key can close ShowWhenLocked activity.
   1121             pressBackButton();
   1122 
   1123             // If device is unlocked, there might have ShowWhenLocked activity runs on,
   1124             // use home key to clear all activity at foreground.
   1125             pressHomeButton();
   1126             sleepDevice();
   1127             wakeUpDevice();
   1128             unlockDevice();
   1129         }
   1130 
   1131         /**
   1132          * Returns whether the lock screen is disabled.
   1133          *
   1134          * @return true if the lock screen is disabled, false otherwise.
   1135          */
   1136         private boolean isLockDisabled() {
   1137             final String isLockDisabled = runCommandAndPrintOutput(
   1138                     "locksettings get-disabled").trim();
   1139             return !"null".equals(isLockDisabled) && Boolean.parseBoolean(isLockDisabled);
   1140         }
   1141 
   1142         /**
   1143          * Disable the lock screen.
   1144          *
   1145          * @param lockDisabled true if should disable, false otherwise.
   1146          */
   1147         protected void setLockDisabled(boolean lockDisabled) {
   1148             runCommandAndPrintOutput("locksettings set-disabled " + lockDisabled);
   1149         }
   1150     }
   1151 
   1152     /** Helper class to save, set & wait, and restore rotation related preferences. */
   1153     protected class RotationSession extends SettingsSession<Integer> {
   1154         private final SettingsSession<Integer> mUserRotation;
   1155         private final HandlerThread mThread;
   1156         private final Handler mRunnableHandler;
   1157         private final SettingsObserver mRotationObserver;
   1158         private int mPreviousDegree;
   1159 
   1160         public RotationSession() {
   1161             // Save accelerometer_rotation preference.
   1162             super(Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
   1163                     Settings.System::getInt, Settings.System::putInt);
   1164             mUserRotation = new SettingsSession<>(
   1165                     Settings.System.getUriFor(Settings.System.USER_ROTATION),
   1166                     Settings.System::getInt, Settings.System::putInt);
   1167 
   1168             mThread = new HandlerThread("Observer_Thread");
   1169             mThread.start();
   1170             mRunnableHandler = new Handler(mThread.getLooper());
   1171             mRotationObserver = new SettingsObserver(mRunnableHandler);
   1172 
   1173             mPreviousDegree = mUserRotation.get();
   1174             // Disable accelerometer_rotation.
   1175             super.set(0);
   1176         }
   1177 
   1178         @Override
   1179         public void set(@NonNull Integer value) {
   1180             // When the rotation is locked and the SystemUI receives the rotation becoming 0deg, it
   1181             // will call freezeRotation to WMS, which will cause USER_ROTATION be set to zero again.
   1182             // In order to prevent our test target from being overwritten by SystemUI during
   1183             // rotation test, wait for the USER_ROTATION changed then continue testing.
   1184             final boolean waitSystemUI = value == ROTATION_0 && mPreviousDegree != ROTATION_0;
   1185             if (waitSystemUI) {
   1186                 mRotationObserver.observe();
   1187             }
   1188             mUserRotation.set(value);
   1189             mPreviousDegree = value;
   1190 
   1191             if (waitSystemUI) {
   1192                 waitForRotationNotified();
   1193             }
   1194             // Wait for settling rotation.
   1195             mAmWmState.waitForRotation(value);
   1196 
   1197             if (waitSystemUI) {
   1198                 mRotationObserver.stopObserver();
   1199             }
   1200         }
   1201 
   1202         @Override
   1203         public void close() throws Exception {
   1204             mThread.quitSafely();
   1205             mUserRotation.close();
   1206             // Restore accelerometer_rotation preference.
   1207             super.close();
   1208         }
   1209 
   1210         private void waitForRotationNotified() {
   1211             for (int retry = 1; retry <= 5; retry++) {
   1212                 // There will receive USER_ROTATION changed twice because when the device rotates to
   1213                 // 0deg, RotationContextButton will also set ROTATION_0 again.
   1214                 if (mRotationObserver.count == 2) {
   1215                     return;
   1216                 }
   1217                 logAlways("waitForRotationNotified retry=" + retry);
   1218                 SystemClock.sleep(500);
   1219             }
   1220             logE("waitForRotationNotified skip");
   1221         }
   1222 
   1223         private class SettingsObserver extends ContentObserver {
   1224             int count;
   1225 
   1226             SettingsObserver(Handler handler) { super(handler); }
   1227 
   1228             void observe() {
   1229                 count = 0;
   1230                 final ContentResolver resolver = mContext.getContentResolver();
   1231                 resolver.registerContentObserver(Settings.System.getUriFor(
   1232                         Settings.System.USER_ROTATION), false, this);
   1233             }
   1234 
   1235             void stopObserver() {
   1236                 count = 0;
   1237                 final ContentResolver resolver = mContext.getContentResolver();
   1238                 resolver.unregisterContentObserver(this);
   1239             }
   1240 
   1241             @Override
   1242             public void onChange(boolean selfChange) {
   1243                 count++;
   1244             }
   1245         }
   1246     }
   1247 
   1248     /**
   1249      * Returns whether the test device respects settings of locked user rotation mode.
   1250      *
   1251      * The method sets the locked user rotation settings to the rotation that rotates the display by
   1252      * 180 degrees and checks if the actual display rotation changes after that.
   1253      *
   1254      * This is a necessary assumption check before leveraging user rotation mode to force display
   1255      * rotation, because there is no requirement that an Android device that supports both
   1256      * orientations needs to support user rotation mode.
   1257      *
   1258      * @param session   the rotation session used to set user rotation
   1259      * @param displayId the display ID to check rotation against
   1260      * @return {@code true} if test device respects settings of locked user rotation mode;
   1261      * {@code false} if not.
   1262      */
   1263     protected boolean supportsLockedUserRotation(RotationSession session, int displayId)
   1264             throws Exception {
   1265         final int origRotation = getDeviceRotation(displayId);
   1266         // Use the same orientation as target rotation to avoid affect of app-requested orientation.
   1267         final int targetRotation = (origRotation + 2) % 4;
   1268         session.set(targetRotation);
   1269         final boolean result = (getDeviceRotation(displayId) == targetRotation);
   1270         session.set(origRotation);
   1271         return result;
   1272     }
   1273 
   1274     protected int getDeviceRotation(int displayId) {
   1275         final String displays = runCommandAndPrintOutput("dumpsys display displays").trim();
   1276         Pattern pattern = Pattern.compile(
   1277                 "(mDisplayId=" + displayId + ")([\\s\\S]*?)(mOverrideDisplayInfo)(.*)"
   1278                         + "(rotation)(\\s+)(\\d+)");
   1279         Matcher matcher = pattern.matcher(displays);
   1280         if (matcher.find()) {
   1281             final String match = matcher.group(7);
   1282             return Integer.parseInt(match);
   1283         }
   1284 
   1285         return INVALID_DEVICE_ROTATION;
   1286     }
   1287 
   1288     /** Empties the test journal so the following events won't be mixed-up with previous records. */
   1289     protected void separateTestJournal() {
   1290         TestJournalContainer.start();
   1291     }
   1292 
   1293     protected static String runCommandAndPrintOutput(String command) {
   1294         final String output = executeShellCommand(command);
   1295         log(output);
   1296         return output;
   1297     }
   1298 
   1299     protected static class LogSeparator {
   1300         private final String mUniqueString;
   1301 
   1302         private LogSeparator() {
   1303             mUniqueString = UUID.randomUUID().toString();
   1304         }
   1305 
   1306         @Override
   1307         public String toString() {
   1308             return mUniqueString;
   1309         }
   1310     }
   1311 
   1312     /**
   1313      * Inserts a log separator so we can always find the starting point from where to evaluate
   1314      * following logs.
   1315      *
   1316      * @return Unique log separator.
   1317      */
   1318     protected LogSeparator separateLogs() {
   1319         final LogSeparator logSeparator = new LogSeparator();
   1320         executeShellCommand("log -t " + LOG_SEPARATOR + " " + logSeparator);
   1321         EventLog.writeEvent(EVENT_LOG_SEPARATOR_TAG, logSeparator.mUniqueString);
   1322         return logSeparator;
   1323     }
   1324 
   1325     protected static String[] getDeviceLogsForComponents(
   1326             LogSeparator logSeparator, String... logTags) {
   1327         String filters = LOG_SEPARATOR + ":I ";
   1328         for (String component : logTags) {
   1329             filters += component + ":I ";
   1330         }
   1331         final String[] result = executeShellCommand("logcat -v brief -d " + filters + " *:S")
   1332                 .split("\\n");
   1333         if (logSeparator == null) {
   1334             return result;
   1335         }
   1336 
   1337         // Make sure that we only check logs after the separator.
   1338         int i = 0;
   1339         boolean lookingForSeparator = true;
   1340         while (i < result.length && lookingForSeparator) {
   1341             if (result[i].contains(logSeparator.toString())) {
   1342                 lookingForSeparator = false;
   1343             }
   1344             i++;
   1345         }
   1346         final String[] filteredResult = new String[result.length - i];
   1347         for (int curPos = 0; i < result.length; curPos++, i++) {
   1348             filteredResult[curPos] = result[i];
   1349         }
   1350         return filteredResult;
   1351     }
   1352 
   1353     protected static List<Event> getEventLogsForComponents(LogSeparator logSeparator, int... tags) {
   1354         List<Event> events = new ArrayList<>();
   1355 
   1356         int[] searchTags = Arrays.copyOf(tags, tags.length + 1);
   1357         searchTags[searchTags.length - 1] = EVENT_LOG_SEPARATOR_TAG;
   1358 
   1359         try {
   1360             EventLog.readEvents(searchTags, events);
   1361         } catch (IOException e) {
   1362             fail("Could not read from event log." + e);
   1363         }
   1364 
   1365         for (Iterator<Event> itr = events.iterator(); itr.hasNext(); ) {
   1366             Event event = itr.next();
   1367             itr.remove();
   1368             if (event.getTag() == EVENT_LOG_SEPARATOR_TAG &&
   1369                     logSeparator.mUniqueString.equals(event.getData())) {
   1370                 break;
   1371             }
   1372         }
   1373         return events;
   1374     }
   1375 
   1376     protected boolean supportsMultiDisplay() {
   1377         return mContext.getPackageManager().hasSystemFeature(
   1378                 FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
   1379     }
   1380 
   1381     /**
   1382      * Base helper class for retrying validator success.
   1383      */
   1384     private abstract static class RetryValidator {
   1385 
   1386         private static final int RETRY_LIMIT = 5;
   1387         private static final long RETRY_INTERVAL = TimeUnit.SECONDS.toMillis(1);
   1388 
   1389         /**
   1390          * @return Error string if validation is failed, null if everything is fine.
   1391          **/
   1392         @Nullable
   1393         protected abstract String validate();
   1394 
   1395         /**
   1396          * Executes {@link #validate()}. Retries {@link #RETRY_LIMIT} times with
   1397          * {@link #RETRY_INTERVAL} interval.
   1398          *
   1399          * @param waitingMessage logging message while waiting validation.
   1400          */
   1401         void assertValidator(String waitingMessage) {
   1402             String resultString = null;
   1403             for (int retry = 1; retry <= RETRY_LIMIT; retry++) {
   1404                 resultString = validate();
   1405                 if (resultString == null) {
   1406                     return;
   1407                 }
   1408                 logAlways(waitingMessage + ": " + resultString);
   1409                 SystemClock.sleep(RETRY_INTERVAL);
   1410             }
   1411             fail(resultString);
   1412         }
   1413     }
   1414 
   1415     static class CountSpec<T> {
   1416         static final int DONT_CARE = Integer.MIN_VALUE;
   1417         static final int EQUALS = 1;
   1418         static final int GREATER_THAN = 2;
   1419         static final int LESS_THAN = 3;
   1420 
   1421         final T mEvent;
   1422         final int mRule;
   1423         final int mCount;
   1424         final String mMessage;
   1425 
   1426         CountSpec(T event, int rule, int count, String message) {
   1427             mEvent = event;
   1428             mRule = count == DONT_CARE ? DONT_CARE : rule;
   1429             mCount = count;
   1430             if (message != null) {
   1431                 mMessage = message;
   1432             } else {
   1433                 switch (rule) {
   1434                     case EQUALS:
   1435                         mMessage = event + " + must equal to " + count;
   1436                         break;
   1437                     case GREATER_THAN:
   1438                         mMessage = event + " + must be greater than " + count;
   1439                         break;
   1440                     case LESS_THAN:
   1441                         mMessage = event + " + must be less than " + count;
   1442                         break;
   1443                     default:
   1444                         mMessage = "Don't care";
   1445                 }
   1446             }
   1447         }
   1448 
   1449         /** @return {@code true} if the given value is satisfied the condition. */
   1450         boolean validate(int value) {
   1451             switch (mRule) {
   1452                 case DONT_CARE:
   1453                     return true;
   1454                 case EQUALS:
   1455                     return value == mCount;
   1456                 case GREATER_THAN:
   1457                     return value > mCount;
   1458                 case LESS_THAN:
   1459                     return value < mCount;
   1460                 default:
   1461             }
   1462             throw new RuntimeException("Unknown CountSpec rule");
   1463         }
   1464     }
   1465 
   1466     static <T> CountSpec<T> countSpec(T event, int rule, int count, String message) {
   1467         return new CountSpec<>(event, rule, count, message);
   1468     }
   1469 
   1470     static <T> CountSpec<T> countSpec(T event, int rule, int count) {
   1471         return new CountSpec<>(event, rule, count, null /* message */);
   1472     }
   1473 
   1474     static void assertLifecycleCounts(ComponentName activityName, String message,
   1475             int createCount, int startCount, int resumeCount, int pauseCount, int stopCount,
   1476             int destroyCount, int configChangeCount) {
   1477         new ActivityLifecycleCounts(activityName).assertCountWithRetry(
   1478                 message,
   1479                 countSpec(ActivityCallback.ON_CREATE, CountSpec.EQUALS, createCount),
   1480                 countSpec(ActivityCallback.ON_START, CountSpec.EQUALS, startCount),
   1481                 countSpec(ActivityCallback.ON_RESUME, CountSpec.EQUALS, resumeCount),
   1482                 countSpec(ActivityCallback.ON_PAUSE, CountSpec.EQUALS, pauseCount),
   1483                 countSpec(ActivityCallback.ON_STOP, CountSpec.EQUALS, stopCount),
   1484                 countSpec(ActivityCallback.ON_DESTROY, CountSpec.EQUALS, destroyCount),
   1485                 countSpec(ActivityCallback.ON_CONFIGURATION_CHANGED, CountSpec.EQUALS,
   1486                         configChangeCount));
   1487     }
   1488 
   1489     static void assertLifecycleCounts(ComponentName activityName,
   1490             int createCount, int startCount, int resumeCount, int pauseCount, int stopCount,
   1491             int destroyCount, int configChangeCount) {
   1492         assertLifecycleCounts(activityName, "Assert lifecycle of " + getLogTag(activityName),
   1493                 createCount, startCount, resumeCount, pauseCount, stopCount,
   1494                 destroyCount, configChangeCount);
   1495     }
   1496 
   1497     static void assertSingleLaunch(ComponentName activityName) {
   1498         assertLifecycleCounts(activityName,
   1499                 "***Waiting for activity create, start, and resume",
   1500                 1 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
   1501                 0 /* pauseCount */, 0 /* stopCount */, 0 /* destroyCount */,
   1502                 CountSpec.DONT_CARE /* configChangeCount */);
   1503     }
   1504 
   1505     static void assertSingleLaunchAndStop(ComponentName activityName) {
   1506         assertLifecycleCounts(activityName,
   1507                 "***Waiting for activity create, start, resume, pause, and stop",
   1508                 1 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
   1509                 1 /* pauseCount */, 1 /* stopCount */, 0 /* destroyCount */,
   1510                 CountSpec.DONT_CARE /* configChangeCount */);
   1511     }
   1512 
   1513     static void assertSingleStartAndStop(ComponentName activityName) {
   1514         assertLifecycleCounts(activityName,
   1515                 "***Waiting for activity start, resume, pause, and stop",
   1516                 0 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
   1517                 1 /* pauseCount */, 1 /* stopCount */, 0 /* destroyCount */,
   1518                 CountSpec.DONT_CARE /* configChangeCount */);
   1519     }
   1520 
   1521     static void assertSingleStart(ComponentName activityName) {
   1522         assertLifecycleCounts(activityName,
   1523                 "***Waiting for activity start and resume",
   1524                 0 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
   1525                 0 /* pauseCount */, 0 /* stopCount */, 0 /* destroyCount */,
   1526                 CountSpec.DONT_CARE /* configChangeCount */);
   1527     }
   1528 
   1529     /** Assert the activity is either relaunched or received configuration changed. */
   1530     static void assertActivityLifecycle(ComponentName activityName, boolean relaunched) {
   1531         new RetryValidator() {
   1532 
   1533             @Nullable
   1534             @Override
   1535             protected String validate() {
   1536                 final String failedReason = checkActivityIsRelaunchedOrConfigurationChanged(
   1537                         getActivityName(activityName),
   1538                         TestJournalContainer.get(activityName).callbacks, relaunched);
   1539                 if (failedReason != null) {
   1540                     return failedReason;
   1541                 }
   1542                 return null;
   1543             }
   1544         }.assertValidator("***Waiting for valid lifecycle state");
   1545     }
   1546 
   1547     /** Assert the activity is either relaunched or received configuration changed. */
   1548     static List<ActivityCallback> assertActivityLifecycle(ActivitySession activitySession,
   1549             boolean relaunched) {
   1550         final String name = activitySession.getName();
   1551         final List<ActivityCallback> callbackHistory = activitySession.takeCallbackHistory();
   1552         String failedReason = checkActivityIsRelaunchedOrConfigurationChanged(
   1553                 name, callbackHistory, relaunched);
   1554         if (failedReason != null) {
   1555             fail(failedReason);
   1556         }
   1557         return callbackHistory;
   1558     }
   1559 
   1560     private static String checkActivityIsRelaunchedOrConfigurationChanged(String name,
   1561             List<ActivityCallback> callbackHistory, boolean relaunched) {
   1562         final ActivityLifecycleCounts lifecycles = new ActivityLifecycleCounts(callbackHistory);
   1563         if (relaunched) {
   1564             return lifecycles.validateCount(
   1565                     countSpec(ActivityCallback.ON_DESTROY, CountSpec.GREATER_THAN, 0,
   1566                             name + " must have been destroyed."),
   1567                     countSpec(ActivityCallback.ON_CREATE, CountSpec.GREATER_THAN, 0,
   1568                             name + " must have been (re)created."));
   1569         }
   1570         return lifecycles.validateCount(
   1571                 countSpec(ActivityCallback.ON_DESTROY, CountSpec.LESS_THAN, 1,
   1572                         name + " must *NOT* have been destroyed."),
   1573                 countSpec(ActivityCallback.ON_CREATE, CountSpec.LESS_THAN, 1,
   1574                         name + " must *NOT* have been (re)created."),
   1575                 countSpec(ActivityCallback.ON_CONFIGURATION_CHANGED, CountSpec.GREATER_THAN, 0,
   1576                                 name + " must have received configuration changed."));
   1577     }
   1578 
   1579     static void assertRelaunchOrConfigChanged(ComponentName activityName, int numRelaunch,
   1580             int numConfigChange) {
   1581         new ActivityLifecycleCounts(activityName).assertCountWithRetry(
   1582                 "***Waiting for relaunch or config changed",
   1583                 countSpec(ActivityCallback.ON_DESTROY, CountSpec.EQUALS, numRelaunch),
   1584                 countSpec(ActivityCallback.ON_CREATE, CountSpec.EQUALS, numRelaunch),
   1585                 countSpec(ActivityCallback.ON_CONFIGURATION_CHANGED, CountSpec.EQUALS,
   1586                         numConfigChange));
   1587     }
   1588 
   1589     static void assertActivityDestroyed(ComponentName activityName) {
   1590         new ActivityLifecycleCounts(activityName).assertCountWithRetry(
   1591                 "***Waiting for activity destroyed",
   1592                 countSpec(ActivityCallback.ON_DESTROY, CountSpec.EQUALS, 1),
   1593                 countSpec(ActivityCallback.ON_CREATE, CountSpec.EQUALS, 0),
   1594                 countSpec(ActivityCallback.ON_CONFIGURATION_CHANGED, CountSpec.EQUALS, 0));
   1595     }
   1596 
   1597     private static final Pattern sCurrentUiModePattern = Pattern.compile("mCurUiMode=0x(\\d+)");
   1598     private static final Pattern sUiModeLockedPattern =
   1599             Pattern.compile("mUiModeLocked=(true|false)");
   1600 
   1601     @Nullable
   1602     SizeInfo getLastReportedSizesForActivity(ComponentName activityName) {
   1603         for (int retry = 1; retry <= 5; retry++) {
   1604             final ConfigInfo result = TestJournalContainer.get(activityName).lastConfigInfo;
   1605             if (result != null && result.sizeInfo != null) {
   1606                 return result.sizeInfo;
   1607             }
   1608             logAlways("***Waiting for sizes to be reported... retry=" + retry);
   1609             SystemClock.sleep(1000);
   1610         }
   1611         logE("***Waiting for activity size failed: activityName=" + getActivityName(activityName));
   1612         return null;
   1613     }
   1614 
   1615     /** Check if a device has display cutout. */
   1616     boolean hasDisplayCutout() {
   1617         // Launch an activity to report cutout state
   1618         separateTestJournal();
   1619         launchActivity(BROADCAST_RECEIVER_ACTIVITY);
   1620 
   1621         // Read the logs to check if cutout is present
   1622         final Boolean displayCutoutPresent = getCutoutStateForActivity(BROADCAST_RECEIVER_ACTIVITY);
   1623         assertNotNull("The activity should report cutout state", displayCutoutPresent);
   1624 
   1625         // Finish activity
   1626         mBroadcastActionTrigger.finishBroadcastReceiverActivity();
   1627         mAmWmState.waitForWithAmState(
   1628                 (state) -> !state.containsActivity(BROADCAST_RECEIVER_ACTIVITY),
   1629                 "Waiting for activity to be removed");
   1630 
   1631         return displayCutoutPresent;
   1632     }
   1633 
   1634     /**
   1635      * Wait for activity to report cutout state in logs and return it. Will return {@code null}
   1636      * after timeout.
   1637      */
   1638     @Nullable
   1639     private Boolean getCutoutStateForActivity(ComponentName activityName) {
   1640         final String logTag = getLogTag(activityName);
   1641         for (int retry = 1; retry <= 5; retry++) {
   1642             final Bundle extras = TestJournalContainer.get(activityName).extras;
   1643             if (extras.containsKey(EXTRA_CUTOUT_EXISTS)) {
   1644                 return extras.getBoolean(EXTRA_CUTOUT_EXISTS);
   1645             }
   1646             logAlways("***Waiting for cutout state to be reported... retry=" + retry);
   1647             SystemClock.sleep(1000);
   1648         }
   1649         logE("***Waiting for activity cutout state failed: activityName=" + logTag);
   1650         return null;
   1651     }
   1652 
   1653     /** Waits for at least one onMultiWindowModeChanged event. */
   1654     ActivityLifecycleCounts waitForOnMultiWindowModeChanged(ComponentName activityName) {
   1655         int retry = 1;
   1656         ActivityLifecycleCounts result;
   1657         do {
   1658             result = new ActivityLifecycleCounts(activityName);
   1659             if (result.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED) >= 1) {
   1660                 return result;
   1661             }
   1662             logAlways("***waitForOnMultiWindowModeChanged... retry=" + retry);
   1663             SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
   1664         } while (retry++ <= 5);
   1665         return result;
   1666     }
   1667 
   1668     static class ActivityLifecycleCounts {
   1669         final int[] mCounts = new int[ActivityCallback.SIZE];
   1670         final int[] mLastIndexes = new int[ActivityCallback.SIZE];
   1671         final List<ActivityCallback> mCallbackHistory;
   1672 
   1673         ActivityLifecycleCounts(ComponentName componentName) {
   1674             this(TestJournalContainer.get(componentName).callbacks);
   1675         }
   1676 
   1677         ActivityLifecycleCounts(List<ActivityCallback> callbacks) {
   1678             mCallbackHistory = callbacks;
   1679             for (int i = 0; i < callbacks.size(); i++) {
   1680                 final ActivityCallback callback = callbacks.get(i);
   1681                 final int ordinal = callback.ordinal();
   1682                 mCounts[ordinal]++;
   1683                 mLastIndexes[ordinal] = i;
   1684             }
   1685         }
   1686 
   1687         int getCount(ActivityCallback callback) {
   1688             return mCounts[callback.ordinal()];
   1689         }
   1690 
   1691         int getLastIndex(ActivityCallback callback) {
   1692             return mLastIndexes[callback.ordinal()];
   1693         }
   1694 
   1695         @SafeVarargs
   1696         final void assertCountWithRetry(String message, CountSpec<ActivityCallback>... countSpecs) {
   1697             new RetryValidator() {
   1698                 @Override
   1699                 protected String validate() {
   1700                     return validateCount(countSpecs);
   1701                 }
   1702             }.assertValidator(message);
   1703         }
   1704 
   1705         @SafeVarargs
   1706         final String validateCount(CountSpec<ActivityCallback>... countSpecs) {
   1707             ArrayList<String> failedReasons = null;
   1708             for (CountSpec<ActivityCallback> spec : countSpecs) {
   1709                 final int realCount = mCounts[spec.mEvent.ordinal()];
   1710                 if (!spec.validate(realCount)) {
   1711                     if (failedReasons == null) {
   1712                         failedReasons = new ArrayList<>();
   1713                     }
   1714                     failedReasons.add(spec.mMessage);
   1715                 }
   1716             }
   1717             return failedReasons == null ? null : String.join("\n", failedReasons);
   1718         }
   1719     }
   1720 
   1721     protected void stopTestPackage(final String packageName) {
   1722         SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(packageName));
   1723     }
   1724 
   1725     protected LaunchActivityBuilder getLaunchActivityBuilder() {
   1726         return new LaunchActivityBuilder(mAmWmState);
   1727     }
   1728 
   1729     protected static class LaunchActivityBuilder implements LaunchProxy {
   1730         private final ActivityAndWindowManagersState mAmWmState;
   1731 
   1732         // The activity to be launched
   1733         private ComponentName mTargetActivity = TEST_ACTIVITY;
   1734         private boolean mUseApplicationContext;
   1735         private boolean mToSide;
   1736         private boolean mRandomData;
   1737         private boolean mNewTask;
   1738         private boolean mMultipleTask;
   1739         private boolean mAllowMultipleInstances = true;
   1740         private int mDisplayId = INVALID_DISPLAY;
   1741         private int mActivityType = ACTIVITY_TYPE_UNDEFINED;
   1742         // A proxy activity that launches other activities including mTargetActivityName
   1743         private ComponentName mLaunchingActivity = LAUNCHING_ACTIVITY;
   1744         private boolean mReorderToFront;
   1745         private boolean mWaitForLaunched;
   1746         private boolean mSuppressExceptions;
   1747         private boolean mWithShellPermission;
   1748         // Use of the following variables indicates that a broadcast receiver should be used instead
   1749         // of a launching activity;
   1750         private ComponentName mBroadcastReceiver;
   1751         private String mBroadcastReceiverAction;
   1752         private int mIntentFlags;
   1753         private Bundle mExtras;
   1754         private LaunchInjector mLaunchInjector;
   1755 
   1756         private enum LauncherType {
   1757             INSTRUMENTATION, LAUNCHING_ACTIVITY, BROADCAST_RECEIVER
   1758         }
   1759 
   1760         private LauncherType mLauncherType = LauncherType.LAUNCHING_ACTIVITY;
   1761 
   1762         public LaunchActivityBuilder(ActivityAndWindowManagersState amWmState) {
   1763             mAmWmState = amWmState;
   1764             mWaitForLaunched = true;
   1765             mWithShellPermission = true;
   1766         }
   1767 
   1768         public LaunchActivityBuilder setToSide(boolean toSide) {
   1769             mToSide = toSide;
   1770             return this;
   1771         }
   1772 
   1773         public LaunchActivityBuilder setRandomData(boolean randomData) {
   1774             mRandomData = randomData;
   1775             return this;
   1776         }
   1777 
   1778         public LaunchActivityBuilder setNewTask(boolean newTask) {
   1779             mNewTask = newTask;
   1780             return this;
   1781         }
   1782 
   1783         public LaunchActivityBuilder setMultipleTask(boolean multipleTask) {
   1784             mMultipleTask = multipleTask;
   1785             return this;
   1786         }
   1787 
   1788         public LaunchActivityBuilder allowMultipleInstances(boolean allowMultipleInstances) {
   1789             mAllowMultipleInstances = allowMultipleInstances;
   1790             return this;
   1791         }
   1792 
   1793         public LaunchActivityBuilder setReorderToFront(boolean reorderToFront) {
   1794             mReorderToFront = reorderToFront;
   1795             return this;
   1796         }
   1797 
   1798         public LaunchActivityBuilder setUseApplicationContext(boolean useApplicationContext) {
   1799             mUseApplicationContext = useApplicationContext;
   1800             return this;
   1801         }
   1802 
   1803         public ComponentName getTargetActivity() {
   1804             return mTargetActivity;
   1805         }
   1806 
   1807         public boolean isTargetActivityTranslucent() {
   1808             return mAmWmState.getAmState().isActivityTranslucent(mTargetActivity);
   1809         }
   1810 
   1811         public LaunchActivityBuilder setTargetActivity(ComponentName targetActivity) {
   1812             mTargetActivity = targetActivity;
   1813             return this;
   1814         }
   1815 
   1816         public LaunchActivityBuilder setDisplayId(int id) {
   1817             mDisplayId = id;
   1818             return this;
   1819         }
   1820 
   1821         public LaunchActivityBuilder setActivityType(int type) {
   1822             mActivityType = type;
   1823             return this;
   1824         }
   1825 
   1826         public LaunchActivityBuilder setLaunchingActivity(ComponentName launchingActivity) {
   1827             mLaunchingActivity = launchingActivity;
   1828             mLauncherType = LauncherType.LAUNCHING_ACTIVITY;
   1829             return this;
   1830         }
   1831 
   1832         public LaunchActivityBuilder setWaitForLaunched(boolean shouldWait) {
   1833             mWaitForLaunched = shouldWait;
   1834             return this;
   1835         }
   1836 
   1837         /** Use broadcast receiver as a launchpad for activities. */
   1838         public LaunchActivityBuilder setUseBroadcastReceiver(final ComponentName broadcastReceiver,
   1839                 final String broadcastAction) {
   1840             mBroadcastReceiver = broadcastReceiver;
   1841             mBroadcastReceiverAction = broadcastAction;
   1842             mLauncherType = LauncherType.BROADCAST_RECEIVER;
   1843             return this;
   1844         }
   1845 
   1846         /** Use {@link android.app.Instrumentation} as a launchpad for activities. */
   1847         public LaunchActivityBuilder setUseInstrumentation() {
   1848             mLauncherType = LauncherType.INSTRUMENTATION;
   1849             // Calling startActivity() from outside of an Activity context requires the
   1850             // FLAG_ACTIVITY_NEW_TASK flag.
   1851             setNewTask(true);
   1852             return this;
   1853         }
   1854 
   1855         public LaunchActivityBuilder setSuppressExceptions(boolean suppress) {
   1856             mSuppressExceptions = suppress;
   1857             return this;
   1858         }
   1859 
   1860         public LaunchActivityBuilder setWithShellPermission(boolean withShellPermission) {
   1861             mWithShellPermission = withShellPermission;
   1862             return this;
   1863         }
   1864 
   1865         @Override
   1866         public boolean shouldWaitForLaunched() {
   1867             return mWaitForLaunched;
   1868         }
   1869 
   1870         public LaunchActivityBuilder setIntentFlags(int flags) {
   1871             mIntentFlags = flags;
   1872             return this;
   1873         }
   1874 
   1875         public LaunchActivityBuilder setIntentExtra(Consumer<Bundle> extrasConsumer) {
   1876             if (extrasConsumer != null) {
   1877                 mExtras = new Bundle();
   1878                 extrasConsumer.accept(mExtras);
   1879             }
   1880             return this;
   1881         }
   1882 
   1883         @Override
   1884         public Bundle getExtras() {
   1885             return mExtras;
   1886         }
   1887 
   1888         @Override
   1889         public void setLaunchInjector(LaunchInjector injector) {
   1890             mLaunchInjector = injector;
   1891         }
   1892 
   1893         @Override
   1894         public void execute() {
   1895             switch (mLauncherType) {
   1896                 case INSTRUMENTATION:
   1897                     if (mWithShellPermission) {
   1898                         SystemUtil.runWithShellPermissionIdentity(this::launchUsingInstrumentation);
   1899                     } else {
   1900                         launchUsingInstrumentation();
   1901                     }
   1902                     break;
   1903                 case LAUNCHING_ACTIVITY:
   1904                 case BROADCAST_RECEIVER:
   1905                     launchUsingShellCommand();
   1906             }
   1907 
   1908             if (mWaitForLaunched) {
   1909                 mAmWmState.waitForValidState(mTargetActivity);
   1910             }
   1911         }
   1912 
   1913         /** Launch an activity using instrumentation. */
   1914         private void launchUsingInstrumentation() {
   1915             final Bundle b = new Bundle();
   1916             b.putBoolean(KEY_USE_INSTRUMENTATION, true);
   1917             b.putBoolean(KEY_LAUNCH_ACTIVITY, true);
   1918             b.putBoolean(KEY_LAUNCH_TO_SIDE, mToSide);
   1919             b.putBoolean(KEY_RANDOM_DATA, mRandomData);
   1920             b.putBoolean(KEY_NEW_TASK, mNewTask);
   1921             b.putBoolean(KEY_MULTIPLE_TASK, mMultipleTask);
   1922             b.putBoolean(KEY_MULTIPLE_INSTANCES, mAllowMultipleInstances);
   1923             b.putBoolean(KEY_REORDER_TO_FRONT, mReorderToFront);
   1924             b.putInt(KEY_DISPLAY_ID, mDisplayId);
   1925             b.putInt(KEY_ACTIVITY_TYPE, mActivityType);
   1926             b.putBoolean(KEY_USE_APPLICATION_CONTEXT, mUseApplicationContext);
   1927             b.putString(KEY_TARGET_COMPONENT, getActivityName(mTargetActivity));
   1928             b.putBoolean(KEY_SUPPRESS_EXCEPTIONS, mSuppressExceptions);
   1929             b.putInt(KEY_INTENT_FLAGS, mIntentFlags);
   1930             b.putBundle(KEY_INTENT_EXTRAS, getExtras());
   1931             final Context context = getInstrumentation().getContext();
   1932             launchActivityFromExtras(context, b, mLaunchInjector);
   1933         }
   1934 
   1935         /** Build and execute a shell command to launch an activity. */
   1936         private void launchUsingShellCommand() {
   1937             StringBuilder commandBuilder = new StringBuilder();
   1938             if (mBroadcastReceiver != null && mBroadcastReceiverAction != null) {
   1939                 // Use broadcast receiver to launch the target.
   1940                 commandBuilder.append("am broadcast -a ").append(mBroadcastReceiverAction)
   1941                         .append(" -p ").append(mBroadcastReceiver.getPackageName())
   1942                         // Include stopped packages
   1943                         .append(" -f 0x00000020");
   1944             } else {
   1945                 // Use launching activity to launch the target.
   1946                 commandBuilder.append(getAmStartCmd(mLaunchingActivity))
   1947                         .append(" -f 0x20000020");
   1948             }
   1949 
   1950             // Add a flag to ensure we actually mean to launch an activity.
   1951             commandBuilder.append(" --ez " + KEY_LAUNCH_ACTIVITY + " true");
   1952 
   1953             if (mToSide) {
   1954                 commandBuilder.append(" --ez " + KEY_LAUNCH_TO_SIDE + " true");
   1955             }
   1956             if (mRandomData) {
   1957                 commandBuilder.append(" --ez " + KEY_RANDOM_DATA + " true");
   1958             }
   1959             if (mNewTask) {
   1960                 commandBuilder.append(" --ez " + KEY_NEW_TASK + " true");
   1961             }
   1962             if (mMultipleTask) {
   1963                 commandBuilder.append(" --ez " + KEY_MULTIPLE_TASK + " true");
   1964             }
   1965             if (mAllowMultipleInstances) {
   1966                 commandBuilder.append(" --ez " + KEY_MULTIPLE_INSTANCES + " true");
   1967             }
   1968             if (mReorderToFront) {
   1969                 commandBuilder.append(" --ez " + KEY_REORDER_TO_FRONT + " true");
   1970             }
   1971             if (mDisplayId != INVALID_DISPLAY) {
   1972                 commandBuilder.append(" --ei " + KEY_DISPLAY_ID + " ").append(mDisplayId);
   1973             }
   1974             if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
   1975                 commandBuilder.append(" --ei " + KEY_ACTIVITY_TYPE + " ").append(mActivityType);
   1976             }
   1977 
   1978             if (mUseApplicationContext) {
   1979                 commandBuilder.append(" --ez " + KEY_USE_APPLICATION_CONTEXT + " true");
   1980             }
   1981 
   1982             if (mTargetActivity != null) {
   1983                 // {@link ActivityLauncher} parses this extra string by
   1984                 // {@link ComponentName#unflattenFromString(String)}.
   1985                 commandBuilder.append(" --es " + KEY_TARGET_COMPONENT + " ")
   1986                         .append(getActivityName(mTargetActivity));
   1987             }
   1988 
   1989             if (mSuppressExceptions) {
   1990                 commandBuilder.append(" --ez " + KEY_SUPPRESS_EXCEPTIONS + " true");
   1991             }
   1992 
   1993             if (mIntentFlags != 0) {
   1994                 commandBuilder.append(" --ei " + KEY_INTENT_FLAGS + " ").append(mIntentFlags);
   1995             }
   1996 
   1997             if (mLaunchInjector != null) {
   1998                 mLaunchInjector.setupShellCommand(commandBuilder);
   1999             }
   2000             executeShellCommand(commandBuilder.toString());
   2001         }
   2002     }
   2003 
   2004     // Activity used in place of recents when home is the recents component.
   2005     public static class SideActivity extends Activity {
   2006     }
   2007 }
   2008