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.cts; 18 19 import com.android.ddmlib.Log.LogLevel; 20 import com.android.tradefed.device.CollectingOutputReceiver; 21 import com.android.tradefed.device.DeviceNotAvailableException; 22 import com.android.tradefed.device.ITestDevice; 23 import com.android.tradefed.log.LogUtil.CLog; 24 import com.android.tradefed.result.InputStreamSource; 25 import com.android.tradefed.testtype.DeviceTestCase; 26 27 import java.awt.*; 28 import java.awt.image.BufferedImage; 29 import java.lang.Exception; 30 import java.lang.Integer; 31 import java.lang.String; 32 import java.util.HashSet; 33 import java.util.List; 34 import java.util.UUID; 35 import java.util.regex.Matcher; 36 import java.util.regex.Pattern; 37 38 import static android.server.cts.StateLogger.log; 39 import static android.server.cts.StateLogger.logE; 40 41 import android.server.cts.ActivityManagerState.ActivityStack; 42 43 import javax.imageio.ImageIO; 44 45 public abstract class ActivityManagerTestBase extends DeviceTestCase { 46 private static final boolean PRETEND_DEVICE_SUPPORTS_PIP = false; 47 private static final boolean PRETEND_DEVICE_SUPPORTS_FREEFORM = false; 48 private static final String LOG_SEPARATOR = "LOG_SEPARATOR"; 49 50 // Constants copied from ActivityManager.StackId. If they are changed there, these must be 51 // updated. 52 /** Invalid stack ID. */ 53 public static final int INVALID_STACK_ID = -1; 54 55 /** First static stack ID. */ 56 public static final int FIRST_STATIC_STACK_ID = 0; 57 58 /** Home activity stack ID. */ 59 public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID; 60 61 /** ID of stack where fullscreen activities are normally launched into. */ 62 public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1; 63 64 /** ID of stack where freeform/resized activities are normally launched into. */ 65 public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1; 66 67 /** ID of stack that occupies a dedicated region of the screen. */ 68 public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1; 69 70 /** ID of stack that always on top (always visible) when it exist. */ 71 public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1; 72 73 /** Recents activity stack ID. */ 74 public static final int RECENTS_STACK_ID = PINNED_STACK_ID + 1; 75 76 /** Assistant activity stack ID. This stack is fullscreen and non-resizeable. */ 77 public static final int ASSISTANT_STACK_ID = RECENTS_STACK_ID + 1; 78 79 protected static final int[] ALL_STACK_IDS_BUT_HOME = { 80 FULLSCREEN_WORKSPACE_STACK_ID, FREEFORM_WORKSPACE_STACK_ID, DOCKED_STACK_ID, 81 PINNED_STACK_ID, ASSISTANT_STACK_ID 82 }; 83 84 protected static final int[] ALL_STACK_IDS_BUT_HOME_AND_FULLSCREEN = { 85 FREEFORM_WORKSPACE_STACK_ID, DOCKED_STACK_ID, PINNED_STACK_ID, ASSISTANT_STACK_ID 86 }; 87 88 private static final String TASK_ID_PREFIX = "taskId"; 89 90 private static final String AM_STACK_LIST = "am stack list"; 91 92 private static final String AM_FORCE_STOP_TEST_PACKAGE = "am force-stop android.server.cts"; 93 private static final String AM_FORCE_STOP_SECOND_TEST_PACKAGE 94 = "am force-stop android.server.cts.second"; 95 private static final String AM_FORCE_STOP_THIRD_TEST_PACKAGE 96 = "am force-stop android.server.cts.third"; 97 98 private static final String AM_REMOVE_STACK = "am stack remove "; 99 100 protected static final String AM_START_HOME_ACTIVITY_COMMAND = 101 "am start -a android.intent.action.MAIN -c android.intent.category.HOME"; 102 103 protected static final String AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND = 104 "am stack move-top-activity-to-pinned-stack 1 0 0 500 500"; 105 106 static final String LAUNCHING_ACTIVITY = "LaunchingActivity"; 107 static final String ALT_LAUNCHING_ACTIVITY = "AltLaunchingActivity"; 108 static final String BROADCAST_RECEIVER_ACTIVITY = "BroadcastReceiverActivity"; 109 110 /** Broadcast shell command for finishing {@link BroadcastReceiverActivity}. */ 111 static final String FINISH_ACTIVITY_BROADCAST 112 = "am broadcast -a trigger_broadcast --ez finish true"; 113 114 /** Broadcast shell command for finishing {@link BroadcastReceiverActivity}. */ 115 static final String MOVE_TASK_TO_BACK_BROADCAST 116 = "am broadcast -a trigger_broadcast --ez moveToBack true"; 117 118 private static final String AM_RESIZE_DOCKED_STACK = "am stack resize-docked-stack "; 119 private static final String AM_RESIZE_STACK = "am stack resize "; 120 121 static final String AM_MOVE_TASK = "am stack move-task "; 122 123 private static final String AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW = 124 "am supports-split-screen-multi-window"; 125 private static final String AM_NO_HOME_SCREEN = "am no-home-screen"; 126 127 private static final String INPUT_KEYEVENT_HOME = "input keyevent 3"; 128 private static final String INPUT_KEYEVENT_BACK = "input keyevent 4"; 129 private static final String INPUT_KEYEVENT_APP_SWITCH = "input keyevent 187"; 130 public static final String INPUT_KEYEVENT_WINDOW = "input keyevent 171"; 131 132 private static final String LOCK_CREDENTIAL = "1234"; 133 134 private static final int INVALID_DISPLAY_ID = -1; 135 136 private static final String DEFAULT_COMPONENT_NAME = "android.server.cts"; 137 138 static String componentName = DEFAULT_COMPONENT_NAME; 139 140 protected static final int INVALID_DEVICE_ROTATION = -1; 141 142 /** A reference to the device under test. */ 143 protected ITestDevice mDevice; 144 145 private HashSet<String> mAvailableFeatures; 146 getAmStartCmd(final String activityName)147 protected static String getAmStartCmd(final String activityName) { 148 return "am start -n " + getActivityComponentName(activityName); 149 } 150 151 /** 152 * @return the am command to start the given activity with the following extra key/value pairs. 153 * {@param keyValuePairs} must be a list of arguments defining each key/value extra. 154 */ getAmStartCmd(final String activityName, final String... keyValuePairs)155 protected static String getAmStartCmd(final String activityName, 156 final String... keyValuePairs) { 157 String base = getAmStartCmd(activityName); 158 if (keyValuePairs.length % 2 != 0) { 159 throw new RuntimeException("keyValuePairs must be pairs of key/value arguments"); 160 } 161 for (int i = 0; i < keyValuePairs.length; i += 2) { 162 base += " --es " + keyValuePairs[i] + " " + keyValuePairs[i + 1]; 163 } 164 return base; 165 } 166 getAmStartCmd(final String activityName, final int displayId)167 protected static String getAmStartCmd(final String activityName, final int displayId) { 168 return "am start -n " + getActivityComponentName(activityName) + " -f 0x18000000" 169 + " --display " + displayId; 170 } 171 getAmStartCmdInNewTask(final String activityName)172 protected static String getAmStartCmdInNewTask(final String activityName) { 173 return "am start -n " + getActivityComponentName(activityName) + " -f 0x18000000"; 174 } 175 getAmStartCmdOverHome(final String activityName)176 protected static String getAmStartCmdOverHome(final String activityName) { 177 return "am start --activity-task-on-home -n " + getActivityComponentName(activityName); 178 } 179 getOrientationBroadcast(int orientation)180 protected static String getOrientationBroadcast(int orientation) { 181 return "am broadcast -a trigger_broadcast --ei orientation " + orientation; 182 } 183 getActivityComponentName(final String activityName)184 static String getActivityComponentName(final String activityName) { 185 return getActivityComponentName(componentName, activityName); 186 } 187 isFullyQualifiedActivityName(String name)188 private static boolean isFullyQualifiedActivityName(String name) { 189 return name != null && name.contains("."); 190 } 191 getActivityComponentName(final String packageName, final String activityName)192 static String getActivityComponentName(final String packageName, final String activityName) { 193 return packageName + "/" + (isFullyQualifiedActivityName(activityName) ? "" : ".") + 194 activityName; 195 } 196 197 // A little ugly, but lets avoid having to strip static everywhere for 198 // now. setComponentName(String name)199 public static void setComponentName(String name) { 200 componentName = name; 201 } 202 getBaseWindowName()203 static String getBaseWindowName() { 204 return getBaseWindowName(componentName); 205 } 206 getBaseWindowName(final String packageName)207 static String getBaseWindowName(final String packageName) { 208 return getBaseWindowName(packageName, true /*prependPackageName*/); 209 } 210 getBaseWindowName(final String packageName, boolean prependPackageName)211 static String getBaseWindowName(final String packageName, boolean prependPackageName) { 212 return packageName + "/" + (prependPackageName ? packageName + "." : ""); 213 } 214 getWindowName(final String activityName)215 static String getWindowName(final String activityName) { 216 return getWindowName(componentName, activityName); 217 } 218 getWindowName(final String packageName, final String activityName)219 static String getWindowName(final String packageName, final String activityName) { 220 return getBaseWindowName(packageName, !isFullyQualifiedActivityName(activityName)) 221 + activityName; 222 } 223 224 protected ActivityAndWindowManagersState mAmWmState = new ActivityAndWindowManagersState(); 225 226 private int mInitialAccelerometerRotation; 227 private int mUserRotation; 228 private float mFontScale; 229 230 private SurfaceTraceReceiver mSurfaceTraceReceiver; 231 private Thread mSurfaceTraceThread; 232 installSurfaceObserver(SurfaceTraceReceiver.SurfaceObserver observer)233 void installSurfaceObserver(SurfaceTraceReceiver.SurfaceObserver observer) { 234 mSurfaceTraceReceiver = new SurfaceTraceReceiver(observer); 235 mSurfaceTraceThread = new Thread() { 236 @Override 237 public void run() { 238 try { 239 mDevice.executeShellCommand("wm surface-trace", mSurfaceTraceReceiver); 240 } catch (DeviceNotAvailableException e) { 241 logE("Device not available: " + e.toString()); 242 } 243 } 244 }; 245 mSurfaceTraceThread.start(); 246 } 247 removeSurfaceObserver()248 void removeSurfaceObserver() { 249 mSurfaceTraceReceiver.cancel(); 250 mSurfaceTraceThread.interrupt(); 251 } 252 253 @Override setUp()254 protected void setUp() throws Exception { 255 super.setUp(); 256 setComponentName(DEFAULT_COMPONENT_NAME); 257 258 // Get the device, this gives a handle to run commands and install APKs. 259 mDevice = getDevice(); 260 wakeUpAndUnlockDevice(); 261 pressHomeButton(); 262 // Remove special stacks. 263 removeStacks(ALL_STACK_IDS_BUT_HOME_AND_FULLSCREEN); 264 // Store rotation settings. 265 mInitialAccelerometerRotation = getAccelerometerRotation(); 266 mUserRotation = getUserRotation(); 267 mFontScale = getFontScale(); 268 } 269 270 @Override tearDown()271 protected void tearDown() throws Exception { 272 super.tearDown(); 273 try { 274 executeShellCommand(AM_FORCE_STOP_TEST_PACKAGE); 275 executeShellCommand(AM_FORCE_STOP_SECOND_TEST_PACKAGE); 276 executeShellCommand(AM_FORCE_STOP_THIRD_TEST_PACKAGE); 277 // Restore rotation settings to the state they were before test. 278 setAccelerometerRotation(mInitialAccelerometerRotation); 279 setUserRotation(mUserRotation); 280 setFontScale(mFontScale); 281 // Remove special stacks. 282 removeStacks(ALL_STACK_IDS_BUT_HOME_AND_FULLSCREEN); 283 wakeUpAndUnlockDevice(); 284 pressHomeButton(); 285 } catch (DeviceNotAvailableException e) { 286 } 287 } 288 removeStacks(int... stackIds)289 protected void removeStacks(int... stackIds) { 290 try { 291 for (Integer stackId : stackIds) { 292 executeShellCommand(AM_REMOVE_STACK + stackId); 293 } 294 } catch (DeviceNotAvailableException e) { 295 } 296 } 297 executeShellCommand(String command)298 protected String executeShellCommand(String command) throws DeviceNotAvailableException { 299 return executeShellCommand(mDevice, command); 300 } 301 executeShellCommand(ITestDevice device, String command)302 protected static String executeShellCommand(ITestDevice device, String command) 303 throws DeviceNotAvailableException { 304 log("adb shell " + command); 305 return device.executeShellCommand(command); 306 } 307 executeShellCommand(String command, CollectingOutputReceiver outputReceiver)308 protected void executeShellCommand(String command, CollectingOutputReceiver outputReceiver) 309 throws DeviceNotAvailableException { 310 log("adb shell " + command); 311 mDevice.executeShellCommand(command, outputReceiver); 312 } 313 takeScreenshot()314 protected BufferedImage takeScreenshot() throws Exception { 315 final InputStreamSource stream = mDevice.getScreenshot("PNG", false /* rescale */); 316 if (stream == null) { 317 fail("Failed to take screenshot of device"); 318 } 319 return ImageIO.read(stream.createInputStream()); 320 } 321 launchActivityInComponent(final String componentName, final String targetActivityName, final String... keyValuePairs)322 protected void launchActivityInComponent(final String componentName, 323 final String targetActivityName, final String... keyValuePairs) throws Exception { 324 final String originalComponentName = ActivityManagerTestBase.componentName; 325 setComponentName(componentName); 326 launchActivity(targetActivityName, keyValuePairs); 327 setComponentName(originalComponentName); 328 } 329 launchActivity(final String targetActivityName, final String... keyValuePairs)330 protected void launchActivity(final String targetActivityName, final String... keyValuePairs) 331 throws Exception { 332 executeShellCommand(getAmStartCmd(targetActivityName, keyValuePairs)); 333 mAmWmState.waitForValidState(mDevice, targetActivityName); 334 } 335 launchActivityNoWait(final String targetActivityName, final String... keyValuePairs)336 protected void launchActivityNoWait(final String targetActivityName, 337 final String... keyValuePairs) throws Exception { 338 executeShellCommand(getAmStartCmd(targetActivityName, keyValuePairs)); 339 } 340 launchActivityInNewTask(final String targetActivityName)341 protected void launchActivityInNewTask(final String targetActivityName) throws Exception { 342 executeShellCommand(getAmStartCmdInNewTask(targetActivityName)); 343 mAmWmState.waitForValidState(mDevice, targetActivityName); 344 } 345 346 /** 347 * Starts an activity in a new stack. 348 * @return the stack id of the newly created stack. 349 */ launchActivityInNewDynamicStack(final String activityName)350 protected int launchActivityInNewDynamicStack(final String activityName) throws Exception { 351 HashSet<Integer> stackIds = getStackIds(); 352 executeShellCommand("am stack start " + ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID 353 + " " + getActivityComponentName(activityName)); 354 HashSet<Integer> newStackIds = getStackIds(); 355 newStackIds.removeAll(stackIds); 356 if (newStackIds.isEmpty()) { 357 return INVALID_STACK_ID; 358 } else { 359 assertTrue(newStackIds.size() == 1); 360 return newStackIds.iterator().next(); 361 } 362 } 363 364 /** 365 * Returns the set of stack ids. 366 */ getStackIds()367 private HashSet<Integer> getStackIds() throws Exception { 368 mAmWmState.computeState(mDevice, null); 369 final List<ActivityStack> stacks = mAmWmState.getAmState().getStacks(); 370 final HashSet<Integer> stackIds = new HashSet<>(); 371 for (ActivityStack s : stacks) { 372 stackIds.add(s.mStackId); 373 } 374 return stackIds; 375 } 376 launchHomeActivity()377 protected void launchHomeActivity() 378 throws Exception { 379 executeShellCommand(AM_START_HOME_ACTIVITY_COMMAND); 380 mAmWmState.waitForHomeActivityVisible(mDevice); 381 } 382 launchActivityOnDisplay(String targetActivityName, int displayId)383 protected void launchActivityOnDisplay(String targetActivityName, int displayId) 384 throws Exception { 385 executeShellCommand(getAmStartCmd(targetActivityName, displayId)); 386 387 mAmWmState.waitForValidState(mDevice, targetActivityName); 388 } 389 390 /** 391 * Launch specific target activity. It uses existing instance of {@link #LAUNCHING_ACTIVITY}, so 392 * that one should be started first. 393 * @param toSide Launch to side in split-screen. 394 * @param randomData Make intent URI random by generating random data. 395 * @param multipleTask Allow multiple task launch. 396 * @param targetActivityName Target activity to be launched. Only class name should be provided, 397 * package name of {@link #LAUNCHING_ACTIVITY} will be added 398 * automatically. 399 * @param displayId Display id where target activity should be launched. 400 * @throws Exception 401 */ launchActivityFromLaunching(boolean toSide, boolean randomData, boolean multipleTask, String targetActivityName, int displayId)402 protected void launchActivityFromLaunching(boolean toSide, boolean randomData, 403 boolean multipleTask, String targetActivityName, int displayId) throws Exception { 404 StringBuilder commandBuilder = new StringBuilder(getAmStartCmd(LAUNCHING_ACTIVITY)); 405 commandBuilder.append(" -f 0x20000000"); 406 if (toSide) { 407 commandBuilder.append(" --ez launch_to_the_side true"); 408 } 409 if (randomData) { 410 commandBuilder.append(" --ez random_data true"); 411 } 412 if (multipleTask) { 413 commandBuilder.append(" --ez multiple_task true"); 414 } 415 if (targetActivityName != null) { 416 commandBuilder.append(" --es target_activity ").append(targetActivityName); 417 } 418 if (displayId != INVALID_DISPLAY_ID) { 419 commandBuilder.append(" --ei display_id ").append(displayId); 420 } 421 executeShellCommand(commandBuilder.toString()); 422 423 mAmWmState.waitForValidState(mDevice, targetActivityName); 424 } 425 launchActivityInStack(String activityName, int stackId, final String... keyValuePairs)426 protected void launchActivityInStack(String activityName, int stackId, 427 final String... keyValuePairs) throws Exception { 428 executeShellCommand(getAmStartCmd(activityName, keyValuePairs) + " --stack " + stackId); 429 430 mAmWmState.waitForValidState(mDevice, activityName, stackId); 431 } 432 launchActivityInDockStack(String activityName)433 protected void launchActivityInDockStack(String activityName) throws Exception { 434 launchActivity(activityName); 435 // TODO(b/36279415): The way we launch an activity into the docked stack is different from 436 // what the user actually does. Long term we should use 437 // "adb shell input keyevent --longpress _app_swich_key_code_" to trigger a long press on 438 // the recents button which is consistent with what the user does. However, currently sys-ui 439 // does handle FLAG_LONG_PRESS for the app switch key. It just listens for long press on the 440 // view. We need to fix that in sys-ui before we can change this. 441 moveActivityToDockStack(activityName); 442 443 mAmWmState.waitForValidState(mDevice, activityName, DOCKED_STACK_ID); 444 } 445 launchActivityToSide(boolean randomData, boolean multipleTaskFlag, String targetActivity)446 protected void launchActivityToSide(boolean randomData, boolean multipleTaskFlag, 447 String targetActivity) throws Exception { 448 final String activityToLaunch = targetActivity != null ? targetActivity : "TestActivity"; 449 getLaunchActivityBuilder().setToSide(true).setRandomData(randomData) 450 .setMultipleTask(multipleTaskFlag).setTargetActivityName(activityToLaunch) 451 .execute(); 452 453 mAmWmState.waitForValidState(mDevice, activityToLaunch, FULLSCREEN_WORKSPACE_STACK_ID); 454 } 455 moveActivityToDockStack(String activityName)456 protected void moveActivityToDockStack(String activityName) throws Exception { 457 moveActivityToStack(activityName, DOCKED_STACK_ID); 458 } 459 moveActivityToStack(String activityName, int stackId)460 protected void moveActivityToStack(String activityName, int stackId) throws Exception { 461 final int taskId = getActivityTaskId(activityName); 462 final String cmd = AM_MOVE_TASK + taskId + " " + stackId + " true"; 463 executeShellCommand(cmd); 464 465 mAmWmState.waitForValidState(mDevice, activityName, stackId); 466 } 467 resizeActivityTask(String activityName, int left, int top, int right, int bottom)468 protected void resizeActivityTask(String activityName, int left, int top, int right, int bottom) 469 throws Exception { 470 final int taskId = getActivityTaskId(activityName); 471 final String cmd = "am task resize " 472 + taskId + " " + left + " " + top + " " + right + " " + bottom; 473 executeShellCommand(cmd); 474 } 475 resizeDockedStack( int stackWidth, int stackHeight, int taskWidth, int taskHeight)476 protected void resizeDockedStack( 477 int stackWidth, int stackHeight, int taskWidth, int taskHeight) 478 throws DeviceNotAvailableException { 479 executeShellCommand(AM_RESIZE_DOCKED_STACK 480 + "0 0 " + stackWidth + " " + stackHeight 481 + " 0 0 " + taskWidth + " " + taskHeight); 482 } 483 resizeStack(int stackId, int stackLeft, int stackTop, int stackWidth, int stackHeight)484 protected void resizeStack(int stackId, int stackLeft, int stackTop, int stackWidth, 485 int stackHeight) throws DeviceNotAvailableException { 486 executeShellCommand(AM_RESIZE_STACK + String.format("%d %d %d %d %d", stackId, stackLeft, 487 stackTop, stackWidth, stackHeight)); 488 } 489 pressHomeButton()490 protected void pressHomeButton() throws DeviceNotAvailableException { 491 executeShellCommand(INPUT_KEYEVENT_HOME); 492 } 493 pressBackButton()494 protected void pressBackButton() throws DeviceNotAvailableException { 495 executeShellCommand(INPUT_KEYEVENT_BACK); 496 } 497 pressAppSwitchButton()498 protected void pressAppSwitchButton() throws DeviceNotAvailableException { 499 executeShellCommand(INPUT_KEYEVENT_APP_SWITCH); 500 } 501 502 // Utility method for debugging, not used directly here, but useful, so kept around. printStacksAndTasks()503 protected void printStacksAndTasks() throws DeviceNotAvailableException { 504 CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver(); 505 executeShellCommand(AM_STACK_LIST, outputReceiver); 506 String output = outputReceiver.getOutput(); 507 for (String line : output.split("\\n")) { 508 CLog.logAndDisplay(LogLevel.INFO, line); 509 } 510 } 511 getActivityTaskId(String name)512 protected int getActivityTaskId(String name) throws DeviceNotAvailableException { 513 CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver(); 514 executeShellCommand(AM_STACK_LIST, outputReceiver); 515 final String output = outputReceiver.getOutput(); 516 final Pattern activityPattern = Pattern.compile("(.*) " + getWindowName(name) + " (.*)"); 517 for (String line : output.split("\\n")) { 518 Matcher matcher = activityPattern.matcher(line); 519 if (matcher.matches()) { 520 for (String word : line.split("\\s+")) { 521 if (word.startsWith(TASK_ID_PREFIX)) { 522 final String withColon = word.split("=")[1]; 523 return Integer.parseInt(withColon.substring(0, withColon.length() - 1)); 524 } 525 } 526 } 527 } 528 return -1; 529 } 530 supportsVrMode()531 protected boolean supportsVrMode() throws DeviceNotAvailableException { 532 return hasDeviceFeature("android.software.vr.mode") && 533 hasDeviceFeature("android.hardware.vr.high_performance"); 534 } 535 supportsPip()536 protected boolean supportsPip() throws DeviceNotAvailableException { 537 return hasDeviceFeature("android.software.picture_in_picture") 538 || PRETEND_DEVICE_SUPPORTS_PIP; 539 } 540 supportsFreeform()541 protected boolean supportsFreeform() throws DeviceNotAvailableException { 542 return hasDeviceFeature("android.software.freeform_window_management") 543 || PRETEND_DEVICE_SUPPORTS_FREEFORM; 544 } 545 isHandheld()546 protected boolean isHandheld() throws DeviceNotAvailableException { 547 return !hasDeviceFeature("android.software.leanback") 548 && !hasDeviceFeature("android.software.watch"); 549 } 550 supportsSplitScreenMultiWindow()551 protected boolean supportsSplitScreenMultiWindow() throws DeviceNotAvailableException { 552 CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver(); 553 executeShellCommand(AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW, outputReceiver); 554 String output = outputReceiver.getOutput(); 555 return !output.startsWith("false"); 556 } 557 noHomeScreen()558 protected boolean noHomeScreen() throws DeviceNotAvailableException { 559 CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver(); 560 executeShellCommand(AM_NO_HOME_SCREEN, outputReceiver); 561 String output = outputReceiver.getOutput(); 562 return output.startsWith("true"); 563 } 564 hasDeviceFeature(String requiredFeature)565 protected boolean hasDeviceFeature(String requiredFeature) throws DeviceNotAvailableException { 566 if (mAvailableFeatures == null) { 567 // TODO: Move this logic to ITestDevice. 568 final String output = runCommandAndPrintOutput("pm list features"); 569 570 // Extract the id of the new user. 571 mAvailableFeatures = new HashSet<>(); 572 for (String feature: output.split("\\s+")) { 573 // Each line in the output of the command has the format "feature:{FEATURE_VALUE}". 574 String[] tokens = feature.split(":"); 575 assertTrue("\"" + feature + "\" expected to have format feature:{FEATURE_VALUE}", 576 tokens.length > 1); 577 assertEquals(feature, "feature", tokens[0]); 578 mAvailableFeatures.add(tokens[1]); 579 } 580 } 581 boolean result = mAvailableFeatures.contains(requiredFeature); 582 if (!result) { 583 CLog.logAndDisplay(LogLevel.INFO, "Device doesn't support " + requiredFeature); 584 } 585 return result; 586 } 587 isDisplayOn()588 private boolean isDisplayOn() throws DeviceNotAvailableException { 589 final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver(); 590 mDevice.executeShellCommand("dumpsys power", outputReceiver); 591 592 for (String line : outputReceiver.getOutput().split("\\n")) { 593 line = line.trim(); 594 595 final Matcher matcher = sDisplayStatePattern.matcher(line); 596 if (matcher.matches()) { 597 final String state = matcher.group(1); 598 log("power state=" + state); 599 return "ON".equals(state); 600 } 601 } 602 log("power state :("); 603 return false; 604 } 605 sleepDevice()606 protected void sleepDevice() throws DeviceNotAvailableException { 607 int retriesLeft = 5; 608 runCommandAndPrintOutput("input keyevent 26"); 609 do { 610 if (isDisplayOn()) { 611 log("***Waiting for display to turn off..."); 612 try { 613 Thread.sleep(1000); 614 } catch (InterruptedException e) { 615 log(e.toString()); 616 // Well I guess we are not waiting... 617 } 618 } else { 619 break; 620 } 621 } while (retriesLeft-- > 0); 622 } 623 wakeUpAndUnlockDevice()624 protected void wakeUpAndUnlockDevice() throws DeviceNotAvailableException { 625 wakeUpDevice(); 626 unlockDevice(); 627 } 628 wakeUpDevice()629 protected void wakeUpDevice() throws DeviceNotAvailableException { 630 runCommandAndPrintOutput("input keyevent 224"); 631 } 632 unlockDevice()633 protected void unlockDevice() throws DeviceNotAvailableException { 634 runCommandAndPrintOutput("input keyevent 82"); 635 } 636 unlockDeviceWithCredential()637 protected void unlockDeviceWithCredential() throws Exception { 638 runCommandAndPrintOutput("input keyevent 82"); 639 try { 640 Thread.sleep(3000); 641 } catch (InterruptedException e) { 642 //ignored 643 } 644 enterAndConfirmLockCredential(); 645 } 646 enterAndConfirmLockCredential()647 protected void enterAndConfirmLockCredential() throws Exception { 648 // TODO: This should use waitForIdle..but there ain't such a thing on hostside tests, boo :( 649 Thread.sleep(500); 650 651 runCommandAndPrintOutput("input text " + LOCK_CREDENTIAL); 652 runCommandAndPrintOutput("input keyevent KEYCODE_ENTER"); 653 } 654 gotoKeyguard()655 protected void gotoKeyguard() throws DeviceNotAvailableException { 656 sleepDevice(); 657 wakeUpDevice(); 658 } 659 setLockCredential()660 protected void setLockCredential() throws DeviceNotAvailableException { 661 runCommandAndPrintOutput("locksettings set-pin " + LOCK_CREDENTIAL); 662 } 663 removeLockCredential()664 protected void removeLockCredential() throws DeviceNotAvailableException { 665 runCommandAndPrintOutput("locksettings clear --old " + LOCK_CREDENTIAL); 666 } 667 668 /** 669 * Sets the device rotation, value corresponds to one of {@link Surface.ROTATION_0}, 670 * {@link Surface.ROTATION_90}, {@link Surface.ROTATION_180}, {@link Surface.ROTATION_270}. 671 */ setDeviceRotation(int rotation)672 protected void setDeviceRotation(int rotation) throws Exception { 673 setAccelerometerRotation(0); 674 setUserRotation(rotation); 675 mAmWmState.waitForRotation(mDevice, rotation); 676 } 677 getDeviceRotation(int displayId)678 protected int getDeviceRotation(int displayId) throws DeviceNotAvailableException { 679 final String displays = runCommandAndPrintOutput("dumpsys display displays").trim(); 680 Pattern pattern = Pattern.compile( 681 "(mDisplayId=" + displayId + ")([\\s\\S]*)(mOverrideDisplayInfo)(.*)" 682 + "(rotation)(\\s+)(\\d+)"); 683 Matcher matcher = pattern.matcher(displays); 684 while (matcher.find()) { 685 final String match = matcher.group(7); 686 return Integer.parseInt(match); 687 } 688 689 return INVALID_DEVICE_ROTATION; 690 } 691 getAccelerometerRotation()692 private int getAccelerometerRotation() throws DeviceNotAvailableException { 693 final String rotation = 694 runCommandAndPrintOutput("settings get system accelerometer_rotation"); 695 return Integer.parseInt(rotation.trim()); 696 } 697 setAccelerometerRotation(int rotation)698 private void setAccelerometerRotation(int rotation) throws DeviceNotAvailableException { 699 runCommandAndPrintOutput( 700 "settings put system accelerometer_rotation " + rotation); 701 } 702 getUserRotation()703 protected int getUserRotation() throws DeviceNotAvailableException { 704 final String rotation = 705 runCommandAndPrintOutput("settings get system user_rotation").trim(); 706 if ("null".equals(rotation)) { 707 return -1; 708 } 709 return Integer.parseInt(rotation); 710 } 711 setUserRotation(int rotation)712 private void setUserRotation(int rotation) throws DeviceNotAvailableException { 713 if (rotation == -1) { 714 runCommandAndPrintOutput( 715 "settings delete system user_rotation"); 716 } else { 717 runCommandAndPrintOutput( 718 "settings put system user_rotation " + rotation); 719 } 720 } 721 setFontScale(float fontScale)722 protected void setFontScale(float fontScale) throws DeviceNotAvailableException { 723 if (fontScale == 0.0f) { 724 runCommandAndPrintOutput( 725 "settings delete system font_scale"); 726 } else { 727 runCommandAndPrintOutput( 728 "settings put system font_scale " + fontScale); 729 } 730 } 731 getFontScale()732 protected float getFontScale() throws DeviceNotAvailableException { 733 try { 734 final String fontScale = 735 runCommandAndPrintOutput("settings get system font_scale").trim(); 736 return Float.parseFloat(fontScale); 737 } catch (NumberFormatException e) { 738 // If we don't have a valid font scale key, return 0.0f now so 739 // that we delete the key in tearDown(). 740 return 0.0f; 741 } 742 } 743 runCommandAndPrintOutput(String command)744 protected String runCommandAndPrintOutput(String command) throws DeviceNotAvailableException { 745 final String output = executeShellCommand(command); 746 log(output); 747 return output; 748 } 749 750 /** 751 * Tries to clear logcat and inserts log separator in case clearing didn't succeed, so we can 752 * always find the starting point from where to evaluate following logs. 753 * @return Unique log separator. 754 */ clearLogcat()755 protected String clearLogcat() throws DeviceNotAvailableException { 756 mDevice.executeAdbCommand("logcat", "-c"); 757 final String uniqueString = UUID.randomUUID().toString(); 758 executeShellCommand("log -t " + LOG_SEPARATOR + " " + uniqueString); 759 return uniqueString; 760 } 761 assertActivityLifecycle(String activityName, boolean relaunched, String logSeparator)762 void assertActivityLifecycle(String activityName, boolean relaunched, 763 String logSeparator) throws DeviceNotAvailableException { 764 int retriesLeft = 5; 765 String resultString; 766 do { 767 resultString = verifyLifecycleCondition(activityName, logSeparator, relaunched); 768 if (resultString != null) { 769 log("***Waiting for valid lifecycle state: " + resultString); 770 try { 771 Thread.sleep(1000); 772 } catch (InterruptedException e) { 773 log(e.toString()); 774 } 775 } else { 776 break; 777 } 778 } while (retriesLeft-- > 0); 779 780 assertNull(resultString, resultString); 781 } 782 783 /** @return Error string if lifecycle counts don't match, null if everything is fine. */ verifyLifecycleCondition(String activityName, String logSeparator, boolean relaunched)784 private String verifyLifecycleCondition(String activityName, String logSeparator, 785 boolean relaunched) throws DeviceNotAvailableException { 786 final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName, 787 logSeparator); 788 if (relaunched) { 789 if (lifecycleCounts.mDestroyCount < 1) { 790 return activityName + " must have been destroyed. mDestroyCount=" 791 + lifecycleCounts.mDestroyCount; 792 } 793 if (lifecycleCounts.mCreateCount < 1) { 794 return activityName + " must have been (re)created. mCreateCount=" 795 + lifecycleCounts.mCreateCount; 796 } 797 } else { 798 if (lifecycleCounts.mDestroyCount > 0) { 799 return activityName + " must *NOT* have been destroyed. mDestroyCount=" 800 + lifecycleCounts.mDestroyCount; 801 } 802 if (lifecycleCounts.mCreateCount > 0) { 803 return activityName + " must *NOT* have been (re)created. mCreateCount=" 804 + lifecycleCounts.mCreateCount; 805 } 806 if (lifecycleCounts.mConfigurationChangedCount < 1) { 807 return activityName + " must have received configuration changed. " 808 + "mConfigurationChangedCount=" 809 + lifecycleCounts.mConfigurationChangedCount; 810 } 811 } 812 return null; 813 } 814 assertRelaunchOrConfigChanged( String activityName, int numRelaunch, int numConfigChange, String logSeparator)815 protected void assertRelaunchOrConfigChanged( 816 String activityName, int numRelaunch, int numConfigChange, String logSeparator) 817 throws DeviceNotAvailableException { 818 int retriesLeft = 5; 819 String resultString; 820 do { 821 resultString = verifyRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange, 822 logSeparator); 823 if (resultString != null) { 824 log("***Waiting for relaunch or config changed: " + resultString); 825 try { 826 Thread.sleep(1000); 827 } catch (InterruptedException e) { 828 log(e.toString()); 829 } 830 } else { 831 break; 832 } 833 } while (retriesLeft-- > 0); 834 835 assertNull(resultString, resultString); 836 } 837 838 /** @return Error string if lifecycle counts don't match, null if everything is fine. */ verifyRelaunchOrConfigChanged(String activityName, int numRelaunch, int numConfigChange, String logSeparator)839 private String verifyRelaunchOrConfigChanged(String activityName, int numRelaunch, 840 int numConfigChange, String logSeparator) throws DeviceNotAvailableException { 841 final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName, 842 logSeparator); 843 844 if (lifecycleCounts.mDestroyCount != numRelaunch) { 845 return activityName + " has been destroyed " + lifecycleCounts.mDestroyCount 846 + " time(s), expecting " + numRelaunch; 847 } else if (lifecycleCounts.mCreateCount != numRelaunch) { 848 return activityName + " has been (re)created " + lifecycleCounts.mCreateCount 849 + " time(s), expecting " + numRelaunch; 850 } else if (lifecycleCounts.mConfigurationChangedCount != numConfigChange) { 851 return activityName + " has received " + lifecycleCounts.mConfigurationChangedCount 852 + " onConfigurationChanged() calls, expecting " + numConfigChange; 853 } 854 return null; 855 } 856 assertActivityDestroyed(String activityName, String logSeparator)857 protected void assertActivityDestroyed(String activityName, String logSeparator) 858 throws DeviceNotAvailableException { 859 int retriesLeft = 5; 860 String resultString; 861 do { 862 resultString = verifyActivityDestroyed(activityName, logSeparator); 863 if (resultString != null) { 864 log("***Waiting for activity destroyed: " + resultString); 865 try { 866 Thread.sleep(1000); 867 } catch (InterruptedException e) { 868 log(e.toString()); 869 } 870 } else { 871 break; 872 } 873 } while (retriesLeft-- > 0); 874 875 assertNull(resultString, resultString); 876 } 877 878 /** @return Error string if lifecycle counts don't match, null if everything is fine. */ verifyActivityDestroyed(String activityName, String logSeparator)879 private String verifyActivityDestroyed(String activityName, String logSeparator) 880 throws DeviceNotAvailableException { 881 final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName, 882 logSeparator); 883 884 if (lifecycleCounts.mDestroyCount != 1) { 885 return activityName + " has been destroyed " + lifecycleCounts.mDestroyCount 886 + " time(s), expecting single destruction."; 887 } else if (lifecycleCounts.mCreateCount != 0) { 888 return activityName + " has been (re)created " + lifecycleCounts.mCreateCount 889 + " time(s), not expecting any."; 890 } else if (lifecycleCounts.mConfigurationChangedCount != 0) { 891 return activityName + " has received " + lifecycleCounts.mConfigurationChangedCount 892 + " onConfigurationChanged() calls, not expecting any."; 893 } 894 return null; 895 } 896 getDeviceLogsForComponent(String componentName, String logSeparator)897 protected String[] getDeviceLogsForComponent(String componentName, String logSeparator) 898 throws DeviceNotAvailableException { 899 return getDeviceLogsForComponents(new String[]{componentName}, logSeparator); 900 } 901 getDeviceLogsForComponents(final String[] componentNames, String logSeparator)902 protected String[] getDeviceLogsForComponents(final String[] componentNames, 903 String logSeparator) throws DeviceNotAvailableException { 904 String filters = LOG_SEPARATOR + ":I "; 905 for (String component : componentNames) { 906 filters += component + ":I "; 907 } 908 final String[] result = mDevice.executeAdbCommand( 909 "logcat", "-v", "brief", "-d", filters, "*:S").split("\\n"); 910 if (logSeparator == null) { 911 return result; 912 } 913 914 // Make sure that we only check logs after the separator. 915 int i = 0; 916 boolean lookingForSeparator = true; 917 while (i < result.length && lookingForSeparator) { 918 if (result[i].contains(logSeparator)) { 919 lookingForSeparator = false; 920 } 921 i++; 922 } 923 final String[] filteredResult = new String[result.length - i]; 924 for (int curPos = 0; i < result.length; curPos++, i++) { 925 filteredResult[curPos] = result[i]; 926 } 927 return filteredResult; 928 } 929 930 private static final Pattern sCreatePattern = Pattern.compile("(.+): onCreate"); 931 private static final Pattern sResumePattern = Pattern.compile("(.+): onResume"); 932 private static final Pattern sPausePattern = Pattern.compile("(.+): onPause"); 933 private static final Pattern sConfigurationChangedPattern = 934 Pattern.compile("(.+): onConfigurationChanged"); 935 private static final Pattern sMovedToDisplayPattern = 936 Pattern.compile("(.+): onMovedToDisplay"); 937 private static final Pattern sStopPattern = Pattern.compile("(.+): onStop"); 938 private static final Pattern sDestroyPattern = Pattern.compile("(.+): onDestroy"); 939 private static final Pattern sMultiWindowModeChangedPattern = 940 Pattern.compile("(.+): onMultiWindowModeChanged"); 941 private static final Pattern sPictureInPictureModeChangedPattern = 942 Pattern.compile("(.+): onPictureInPictureModeChanged"); 943 private static final Pattern sNewConfigPattern = Pattern.compile( 944 "(.+): config size=\\((\\d+),(\\d+)\\) displaySize=\\((\\d+),(\\d+)\\)" 945 + " metricsSize=\\((\\d+),(\\d+)\\) smallestScreenWidth=(\\d+) densityDpi=(\\d+)" 946 + " orientation=(\\d+)"); 947 private static final Pattern sDisplayStatePattern = 948 Pattern.compile("Display Power: state=(.+)"); 949 950 class ReportedSizes { 951 int widthDp; 952 int heightDp; 953 int displayWidth; 954 int displayHeight; 955 int metricsWidth; 956 int metricsHeight; 957 int smallestWidthDp; 958 int densityDpi; 959 int orientation; 960 961 @Override toString()962 public String toString() { 963 return "ReportedSizes: {widthDp=" + widthDp + " heightDp=" + heightDp 964 + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight 965 + " metricsWidth=" + metricsWidth + " metricsHeight=" + metricsHeight 966 + " smallestWidthDp=" + smallestWidthDp + " densityDpi=" + densityDpi 967 + " orientation=" + orientation + "}"; 968 } 969 970 @Override equals(Object obj)971 public boolean equals(Object obj) { 972 if ( this == obj ) return true; 973 if ( !(obj instanceof ReportedSizes) ) return false; 974 ReportedSizes that = (ReportedSizes) obj; 975 return widthDp == that.widthDp 976 && heightDp == that.heightDp 977 && displayWidth == that.displayWidth 978 && displayHeight == that.displayHeight 979 && metricsWidth == that.metricsWidth 980 && metricsHeight == that.metricsHeight 981 && smallestWidthDp == that.smallestWidthDp 982 && densityDpi == that.densityDpi 983 && orientation == that.orientation; 984 } 985 } 986 getLastReportedSizesForActivity(String activityName, String logSeparator)987 ReportedSizes getLastReportedSizesForActivity(String activityName, String logSeparator) 988 throws DeviceNotAvailableException { 989 int retriesLeft = 5; 990 ReportedSizes result; 991 do { 992 result = readLastReportedSizes(activityName, logSeparator); 993 if (result == null) { 994 log("***Waiting for sizes to be reported..."); 995 try { 996 Thread.sleep(1000); 997 } catch (InterruptedException e) { 998 log(e.toString()); 999 // Well I guess we are not waiting... 1000 } 1001 } else { 1002 break; 1003 } 1004 } while (retriesLeft-- > 0); 1005 return result; 1006 } 1007 readLastReportedSizes(String activityName, String logSeparator)1008 private ReportedSizes readLastReportedSizes(String activityName, String logSeparator) 1009 throws DeviceNotAvailableException { 1010 final String[] lines = getDeviceLogsForComponent(activityName, logSeparator); 1011 for (int i = lines.length - 1; i >= 0; i--) { 1012 final String line = lines[i].trim(); 1013 final Matcher matcher = sNewConfigPattern.matcher(line); 1014 if (matcher.matches()) { 1015 ReportedSizes details = new ReportedSizes(); 1016 details.widthDp = Integer.parseInt(matcher.group(2)); 1017 details.heightDp = Integer.parseInt(matcher.group(3)); 1018 details.displayWidth = Integer.parseInt(matcher.group(4)); 1019 details.displayHeight = Integer.parseInt(matcher.group(5)); 1020 details.metricsWidth = Integer.parseInt(matcher.group(6)); 1021 details.metricsHeight = Integer.parseInt(matcher.group(7)); 1022 details.smallestWidthDp = Integer.parseInt(matcher.group(8)); 1023 details.densityDpi = Integer.parseInt(matcher.group(9)); 1024 details.orientation = Integer.parseInt(matcher.group(10)); 1025 return details; 1026 } 1027 } 1028 return null; 1029 } 1030 1031 class ActivityLifecycleCounts { 1032 int mCreateCount; 1033 int mResumeCount; 1034 int mConfigurationChangedCount; 1035 int mLastConfigurationChangedLineIndex; 1036 int mMovedToDisplayCount; 1037 int mMultiWindowModeChangedCount; 1038 int mLastMultiWindowModeChangedLineIndex; 1039 int mPictureInPictureModeChangedCount; 1040 int mLastPictureInPictureModeChangedLineIndex; 1041 int mPauseCount; 1042 int mStopCount; 1043 int mLastStopLineIndex; 1044 int mDestroyCount; 1045 ActivityLifecycleCounts(String activityName, String logSeparator)1046 public ActivityLifecycleCounts(String activityName, String logSeparator) 1047 throws DeviceNotAvailableException { 1048 int lineIndex = 0; 1049 for (String line : getDeviceLogsForComponent(activityName, logSeparator)) { 1050 line = line.trim(); 1051 lineIndex++; 1052 1053 Matcher matcher = sCreatePattern.matcher(line); 1054 if (matcher.matches()) { 1055 mCreateCount++; 1056 continue; 1057 } 1058 1059 matcher = sResumePattern.matcher(line); 1060 if (matcher.matches()) { 1061 mResumeCount++; 1062 continue; 1063 } 1064 1065 matcher = sConfigurationChangedPattern.matcher(line); 1066 if (matcher.matches()) { 1067 mConfigurationChangedCount++; 1068 mLastConfigurationChangedLineIndex = lineIndex; 1069 continue; 1070 } 1071 1072 matcher = sMovedToDisplayPattern.matcher(line); 1073 if (matcher.matches()) { 1074 mMovedToDisplayCount++; 1075 continue; 1076 } 1077 1078 matcher = sMultiWindowModeChangedPattern.matcher(line); 1079 if (matcher.matches()) { 1080 mMultiWindowModeChangedCount++; 1081 mLastMultiWindowModeChangedLineIndex = lineIndex; 1082 continue; 1083 } 1084 1085 matcher = sPictureInPictureModeChangedPattern.matcher(line); 1086 if (matcher.matches()) { 1087 mPictureInPictureModeChangedCount++; 1088 mLastPictureInPictureModeChangedLineIndex = lineIndex; 1089 continue; 1090 } 1091 1092 matcher = sPausePattern.matcher(line); 1093 if (matcher.matches()) { 1094 mPauseCount++; 1095 continue; 1096 } 1097 1098 matcher = sStopPattern.matcher(line); 1099 if (matcher.matches()) { 1100 mStopCount++; 1101 mLastStopLineIndex = lineIndex; 1102 continue; 1103 } 1104 1105 matcher = sDestroyPattern.matcher(line); 1106 if (matcher.matches()) { 1107 mDestroyCount++; 1108 continue; 1109 } 1110 } 1111 } 1112 } 1113 stopTestCase()1114 protected void stopTestCase() throws Exception { 1115 executeShellCommand("am force-stop " + componentName); 1116 } 1117 getLaunchActivityBuilder()1118 protected LaunchActivityBuilder getLaunchActivityBuilder() { 1119 return new LaunchActivityBuilder(mAmWmState, mDevice); 1120 } 1121 1122 protected static class LaunchActivityBuilder { 1123 private final ActivityAndWindowManagersState mAmWmState; 1124 private final ITestDevice mDevice; 1125 1126 private String mTargetActivityName; 1127 private String mTargetPackage = componentName; 1128 private boolean mToSide; 1129 private boolean mRandomData; 1130 private boolean mNewTask; 1131 private boolean mMultipleTask; 1132 private int mDisplayId = INVALID_DISPLAY_ID; 1133 private String mLaunchingActivityName = LAUNCHING_ACTIVITY; 1134 private boolean mReorderToFront; 1135 private boolean mWaitForLaunched; 1136 LaunchActivityBuilder(ActivityAndWindowManagersState amWmState, ITestDevice device)1137 public LaunchActivityBuilder(ActivityAndWindowManagersState amWmState, 1138 ITestDevice device) { 1139 mAmWmState = amWmState; 1140 mDevice = device; 1141 mWaitForLaunched = true; 1142 } 1143 setToSide(boolean toSide)1144 public LaunchActivityBuilder setToSide(boolean toSide) { 1145 mToSide = toSide; 1146 return this; 1147 } 1148 setRandomData(boolean randomData)1149 public LaunchActivityBuilder setRandomData(boolean randomData) { 1150 mRandomData = randomData; 1151 return this; 1152 } 1153 setNewTask(boolean newTask)1154 public LaunchActivityBuilder setNewTask(boolean newTask) { 1155 mNewTask = newTask; 1156 return this; 1157 } 1158 setMultipleTask(boolean multipleTask)1159 public LaunchActivityBuilder setMultipleTask(boolean multipleTask) { 1160 mMultipleTask = multipleTask; 1161 return this; 1162 } 1163 setReorderToFront(boolean reorderToFront)1164 public LaunchActivityBuilder setReorderToFront(boolean reorderToFront) { 1165 mReorderToFront = reorderToFront; 1166 return this; 1167 } 1168 setTargetActivityName(String name)1169 public LaunchActivityBuilder setTargetActivityName(String name) { 1170 mTargetActivityName = name; 1171 return this; 1172 } 1173 setTargetPackage(String pkg)1174 public LaunchActivityBuilder setTargetPackage(String pkg) { 1175 mTargetPackage = pkg; 1176 return this; 1177 } 1178 setDisplayId(int id)1179 public LaunchActivityBuilder setDisplayId(int id) { 1180 mDisplayId = id; 1181 return this; 1182 } 1183 setLaunchingActivityName(String name)1184 public LaunchActivityBuilder setLaunchingActivityName(String name) { 1185 mLaunchingActivityName = name; 1186 return this; 1187 } 1188 setWaitForLaunched(boolean shouldWait)1189 public LaunchActivityBuilder setWaitForLaunched(boolean shouldWait) { 1190 mWaitForLaunched = shouldWait; 1191 return this; 1192 } 1193 execute()1194 public void execute() throws Exception { 1195 StringBuilder commandBuilder = new StringBuilder(getAmStartCmd(mLaunchingActivityName)); 1196 commandBuilder.append(" -f 0x20000000"); 1197 1198 // Add a flag to ensure we actually mean to launch an activity. 1199 commandBuilder.append(" --ez launch_activity true"); 1200 1201 if (mToSide) { 1202 commandBuilder.append(" --ez launch_to_the_side true"); 1203 } 1204 if (mRandomData) { 1205 commandBuilder.append(" --ez random_data true"); 1206 } 1207 if (mNewTask) { 1208 commandBuilder.append(" --ez new_task true"); 1209 } 1210 if (mMultipleTask) { 1211 commandBuilder.append(" --ez multiple_task true"); 1212 } 1213 if (mReorderToFront) { 1214 commandBuilder.append(" --ez reorder_to_front true"); 1215 } 1216 if (mTargetActivityName != null) { 1217 commandBuilder.append(" --es target_activity ").append(mTargetActivityName); 1218 commandBuilder.append(" --es package_name ").append(mTargetPackage); 1219 } 1220 if (mDisplayId != INVALID_DISPLAY_ID) { 1221 commandBuilder.append(" --ei display_id ").append(mDisplayId); 1222 } 1223 executeShellCommand(mDevice, commandBuilder.toString()); 1224 1225 if (mWaitForLaunched) { 1226 mAmWmState.waitForValidState(mDevice, new String[]{mTargetActivityName}, 1227 null /* stackIds */, false /* compareTaskAndStackBounds */, mTargetPackage); 1228 } 1229 } 1230 } 1231 } 1232