1 /* 2 * Copyright (C) 2024 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.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; 20 import static android.server.wm.ActivityLauncher.KEY_ACTIVITY_TYPE; 21 import static android.server.wm.ActivityLauncher.KEY_DISPLAY_ID; 22 import static android.server.wm.ActivityLauncher.KEY_INTENT_EXTRAS; 23 import static android.server.wm.ActivityLauncher.KEY_INTENT_FLAGS; 24 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY; 25 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_TASK_BEHIND; 26 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_TO_SIDE; 27 import static android.server.wm.ActivityLauncher.KEY_MULTIPLE_INSTANCES; 28 import static android.server.wm.ActivityLauncher.KEY_MULTIPLE_TASK; 29 import static android.server.wm.ActivityLauncher.KEY_NEW_TASK; 30 import static android.server.wm.ActivityLauncher.KEY_RANDOM_DATA; 31 import static android.server.wm.ActivityLauncher.KEY_REORDER_TO_FRONT; 32 import static android.server.wm.ActivityLauncher.KEY_SUPPRESS_EXCEPTIONS; 33 import static android.server.wm.ActivityLauncher.KEY_TARGET_COMPONENT; 34 import static android.server.wm.ActivityLauncher.KEY_TASK_DISPLAY_AREA_FEATURE_ID; 35 import static android.server.wm.ActivityLauncher.KEY_USE_APPLICATION_CONTEXT; 36 import static android.server.wm.ActivityLauncher.KEY_WINDOWING_MODE; 37 import static android.server.wm.ActivityLauncher.launchActivityFromExtras; 38 import static android.server.wm.CommandSession.KEY_FORWARD; 39 import static android.server.wm.ComponentNameUtils.getActivityName; 40 import static android.server.wm.ShellCommandHelper.executeShellCommand; 41 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY; 42 import static android.server.wm.app.Components.LaunchingActivity.KEY_FINISH_BEFORE_LAUNCH; 43 import static android.server.wm.app.Components.TEST_ACTIVITY; 44 import static android.view.Display.INVALID_DISPLAY; 45 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; 46 47 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 48 49 import android.app.ActivityOptions; 50 import android.content.ComponentName; 51 import android.content.Context; 52 import android.content.Intent; 53 import android.os.Bundle; 54 import android.os.Process; 55 56 import androidx.annotation.NonNull; 57 import androidx.annotation.Nullable; 58 59 import java.util.function.Consumer; 60 61 public class LaunchActivityBuilder implements CommandSession.LaunchProxy { 62 @NonNull 63 private final WindowManagerStateHelper mAmWmState; 64 65 // The activity to be launched. 66 @NonNull 67 private ComponentName mTargetActivity = TEST_ACTIVITY; 68 69 private boolean mUseApplicationContext; 70 private boolean mToSide; 71 private boolean mRandomData; 72 private boolean mNewTask; 73 private boolean mMultipleTask; 74 private boolean mAllowMultipleInstances = true; 75 private boolean mLaunchTaskBehind; 76 private boolean mFinishBeforeLaunch; 77 private int mDisplayId = INVALID_DISPLAY; 78 private int mWindowingMode = -1; 79 private int mActivityType = ACTIVITY_TYPE_UNDEFINED; 80 81 // A proxy activity that launches other activities including mTargetActivityName. 82 @NonNull 83 private ComponentName mLaunchingActivity = LAUNCHING_ACTIVITY; 84 85 private boolean mReorderToFront; 86 private boolean mWaitForLaunched; 87 private boolean mSuppressExceptions; 88 private boolean mWithShellPermission; 89 90 // Use of the following variables indicates that a broadcast receiver should be used instead 91 // of a launching activity. 92 @Nullable 93 private ComponentName mBroadcastReceiver; 94 @Nullable 95 private String mBroadcastReceiverAction; 96 private int mIntentFlags; 97 @Nullable 98 private Bundle mExtras; 99 @Nullable 100 private CommandSession.LaunchInjector mLaunchInjector; 101 @Nullable 102 private CommandSession.ActivitySessionClient mActivitySessionClient; 103 private int mLaunchTaskDisplayAreaFeatureId = FEATURE_UNDEFINED; 104 105 private enum LauncherType { 106 INSTRUMENTATION, 107 LAUNCHING_ACTIVITY, 108 BROADCAST_RECEIVER, 109 } 110 111 @NonNull 112 private LauncherType mLauncherType = LauncherType.LAUNCHING_ACTIVITY; 113 LaunchActivityBuilder(@onNull WindowManagerStateHelper amWmState)114 public LaunchActivityBuilder(@NonNull WindowManagerStateHelper amWmState) { 115 mAmWmState = amWmState; 116 mWaitForLaunched = true; 117 mWithShellPermission = true; 118 } 119 120 /** 121 * Sets whether the activity should be launched to side in split-screen. 122 * 123 * @param toSide {@code true} if the activity should be launched to the side, 124 * {@code false} otherwise. 125 * @return this LaunchActivityBuilder instance for chaining. 126 * @see ActivityLauncher#KEY_LAUNCH_TO_SIDE 127 */ 128 @NonNull setToSide(boolean toSide)129 public LaunchActivityBuilder setToSide(boolean toSide) { 130 mToSide = toSide; 131 return this; 132 } 133 134 /** 135 * Sets whether random data should be included in the launch intent to be different from other 136 * launch intents. 137 * 138 * @param randomData {@code true} if random data should be included, {@code false} otherwise. 139 * @return this LaunchActivityBuilder instance for chaining. 140 * @see ActivityLauncher#KEY_RANDOM_DATA 141 */ 142 @NonNull setRandomData(boolean randomData)143 public LaunchActivityBuilder setRandomData(boolean randomData) { 144 mRandomData = randomData; 145 return this; 146 } 147 148 /** 149 * Sets whether launch intent should have {@link Intent#FLAG_ACTIVITY_NEW_TASK}. 150 * 151 * @param newTask {@code true} if the launch intent should have 152 * {@link Intent#FLAG_ACTIVITY_NEW_TASK}, {@code false} otherwise. 153 * @return this LaunchActivityBuilder instance for chaining. 154 * @see ActivityLauncher#KEY_NEW_TASK 155 */ 156 @NonNull setNewTask(boolean newTask)157 public LaunchActivityBuilder setNewTask(boolean newTask) { 158 mNewTask = newTask; 159 return this; 160 } 161 162 /** 163 * Sets whether launch intent should have {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}. 164 * 165 * @param multipleTask {@code true} if the launch intent should have 166 * {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, {@code false} otherwise. 167 * @return this LaunchActivityBuilder instance for chaining. 168 * @see ActivityLauncher#KEY_MULTIPLE_TASK 169 */ 170 @NonNull setMultipleTask(boolean multipleTask)171 public LaunchActivityBuilder setMultipleTask(boolean multipleTask) { 172 mMultipleTask = multipleTask; 173 return this; 174 } 175 176 /** 177 * Sets whether need to automatically applies {@link Intent#FLAG_ACTIVITY_NEW_TASK} and 178 * {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK} to the intent when target display id set. 179 * 180 * @param allowMultipleInstances {@code true} if multiple instances are allowed, 181 * {@code false} otherwise. 182 * @return this LaunchActivityBuilder instance for chaining. 183 * @see ActivityLauncher#KEY_MULTIPLE_INSTANCES 184 */ 185 @NonNull allowMultipleInstances(boolean allowMultipleInstances)186 public LaunchActivityBuilder allowMultipleInstances(boolean allowMultipleInstances) { 187 mAllowMultipleInstances = allowMultipleInstances; 188 return this; 189 } 190 191 /** 192 * Sets if the launch task without presented to user. 193 * 194 * @param launchTaskBehind {@code true} if the launch task without presented to user, 195 * {@code false} otherwise. 196 * @return this LaunchActivityBuilder instance for chaining. 197 * @see ActivityOptions#makeTaskLaunchBehind() 198 */ 199 @NonNull setLaunchTaskBehind(boolean launchTaskBehind)200 public LaunchActivityBuilder setLaunchTaskBehind(boolean launchTaskBehind) { 201 mLaunchTaskBehind = launchTaskBehind; 202 return this; 203 } 204 205 /** 206 * Sets whether launch intent should have {@link Intent#FLAG_ACTIVITY_REORDER_TO_FRONT}. 207 * 208 * @param reorderToFront {@code true} if the launch intent should have 209 * {@link Intent#FLAG_ACTIVITY_REORDER_TO_FRONT}, 210 * {@code false} otherwise. 211 * @return this LaunchActivityBuilder instance for chaining. 212 * @see ActivityLauncher#KEY_REORDER_TO_FRONT 213 */ 214 @NonNull setReorderToFront(boolean reorderToFront)215 public LaunchActivityBuilder setReorderToFront(boolean reorderToFront) { 216 mReorderToFront = reorderToFront; 217 return this; 218 } 219 220 /** 221 * Sets whether to use the application context when launching the activity. 222 * 223 * @param useApplicationContext {@code true} if the application context should be used, 224 * {@code false} otherwise. 225 * @return this LaunchActivityBuilder instance for chaining. 226 * @see ActivityLauncher#KEY_USE_APPLICATION_CONTEXT 227 */ 228 @NonNull setUseApplicationContext(boolean useApplicationContext)229 public LaunchActivityBuilder setUseApplicationContext(boolean useApplicationContext) { 230 mUseApplicationContext = useApplicationContext; 231 return this; 232 } 233 234 /** 235 * Sets whether the launching activity should be finished before launching the target activity. 236 * 237 * @param finishBeforeLaunch {@code true} if the launching activity should be finished before 238 * launch, {@code false} otherwise. 239 * @return this LaunchActivityBuilder instance for chaining 240 * @see android.server.wm.app.LaunchingActivity 241 */ 242 @NonNull setFinishBeforeLaunch(boolean finishBeforeLaunch)243 public LaunchActivityBuilder setFinishBeforeLaunch(boolean finishBeforeLaunch) { 244 mFinishBeforeLaunch = finishBeforeLaunch; 245 return this; 246 } 247 248 @NonNull getTargetActivity()249 public ComponentName getTargetActivity() { 250 return mTargetActivity; 251 } 252 isTargetActivityTranslucent()253 public boolean isTargetActivityTranslucent() { 254 return mAmWmState.isActivityTranslucent(mTargetActivity); 255 } 256 257 /** 258 * Sets the target activity to be launched. 259 * 260 * @param targetActivity the component name of the target activity. 261 * @return this LaunchActivityBuilder instance for chaining. 262 * @see ActivityLauncher#KEY_TARGET_COMPONENT 263 */ 264 @NonNull setTargetActivity(@onNull ComponentName targetActivity)265 public LaunchActivityBuilder setTargetActivity(@NonNull ComponentName targetActivity) { 266 mTargetActivity = targetActivity; 267 return this; 268 } 269 270 /** 271 * Sets the display ID on which the activity should be launched. Adding this automatically 272 * applies {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 273 * to the intent. 274 * 275 * @param id the ID of the display. 276 * @return this LaunchActivityBuilder instance for chaining. 277 * @see ActivityLauncher#KEY_DISPLAY_ID 278 */ 279 @NonNull setDisplayId(int id)280 public LaunchActivityBuilder setDisplayId(int id) { 281 mDisplayId = id; 282 return this; 283 } 284 285 /** 286 * Sets the requested windowing mode. 287 * 288 * @param windowingMode the requested windowing mode. 289 * @return this LaunchActivityBuilder instance for chaining. 290 * @see ActivityLauncher#KEY_WINDOWING_MODE 291 */ 292 @NonNull setWindowingMode(int windowingMode)293 public LaunchActivityBuilder setWindowingMode(int windowingMode) { 294 mWindowingMode = windowingMode; 295 return this; 296 } 297 298 /** 299 * Sets the target activity type where activity should be launched as. 300 * 301 * @param type the target activity type. 302 * @return this LaunchActivityBuilder instance for chaining. 303 * @see ActivityLauncher#KEY_ACTIVITY_TYPE 304 */ 305 @NonNull setActivityType(int type)306 public LaunchActivityBuilder setActivityType(int type) { 307 mActivityType = type; 308 return this; 309 } 310 311 /** 312 * Sets the launching activity that will be used to launch the target activity. 313 * 314 * @param launchingActivity The component name of the launching activity. 315 * @return this LaunchActivityBuilder instance for chaining. 316 */ 317 @NonNull setLaunchingActivity(@onNull ComponentName launchingActivity)318 public LaunchActivityBuilder setLaunchingActivity(@NonNull ComponentName launchingActivity) { 319 mLaunchingActivity = launchingActivity; 320 mLauncherType = LauncherType.LAUNCHING_ACTIVITY; 321 return this; 322 } 323 324 /** 325 * Sets whether to wait for the launched activity to be in a valid state. 326 * 327 * @param shouldWait {@code true} if the builder should wait, {@code false} otherwise. 328 * @return this LaunchActivityBuilder instance for chaining. 329 */ 330 @NonNull setWaitForLaunched(boolean shouldWait)331 public LaunchActivityBuilder setWaitForLaunched(boolean shouldWait) { 332 mWaitForLaunched = shouldWait; 333 return this; 334 } 335 336 /** 337 * Sets the TaskDisplayArea feature ID in which the activity should be launched in. 338 * 339 * @param launchTaskDisplayAreaFeatureId the TaskDisplayArea feature ID. 340 * @return this LaunchActivityBuilder instance for chaining. 341 * @see ActivityLauncher#KEY_TASK_DISPLAY_AREA_FEATURE_ID 342 */ 343 @NonNull setLaunchTaskDisplayAreaFeatureId( int launchTaskDisplayAreaFeatureId)344 public LaunchActivityBuilder setLaunchTaskDisplayAreaFeatureId( 345 int launchTaskDisplayAreaFeatureId) { 346 mLaunchTaskDisplayAreaFeatureId = launchTaskDisplayAreaFeatureId; 347 return this; 348 } 349 350 /** 351 * Uses broadcast receiver as a launchpad for activities. 352 * 353 * @param broadcastReceiver the component name of the broadcast receiver. 354 * @param broadcastAction the action of the broadcast. 355 * @return this LaunchActivityBuilder instance for chaining. 356 */ 357 @NonNull setUseBroadcastReceiver( @ullable final ComponentName broadcastReceiver, @Nullable final String broadcastAction)358 public LaunchActivityBuilder setUseBroadcastReceiver( 359 @Nullable final ComponentName broadcastReceiver, 360 @Nullable final String broadcastAction) { 361 mBroadcastReceiver = broadcastReceiver; 362 mBroadcastReceiverAction = broadcastAction; 363 mLauncherType = LauncherType.BROADCAST_RECEIVER; 364 return this; 365 } 366 367 /** 368 * Uses {@link android.app.Instrumentation} as a launchpad for activities. 369 * 370 * @return this LaunchActivityBuilder instance for chaining. 371 */ 372 @NonNull setUseInstrumentation()373 public LaunchActivityBuilder setUseInstrumentation() { 374 mLauncherType = LauncherType.INSTRUMENTATION; 375 // Calling startActivity() from outside of an Activity context requires the 376 // FLAG_ACTIVITY_NEW_TASK flag. 377 setNewTask(true); 378 return this; 379 } 380 381 /** 382 * Sets whether exceptions during launch other then {@link SecurityException} should be 383 * suppressed. A {@link SecurityException} is never thrown, it's always written to logs. 384 * 385 * @param suppress {@code true} to suppress exceptions, {@code false} otherwise. 386 * @return this LaunchActivityBuilder instance for chaining. 387 * @see ActivityLauncher#KEY_SUPPRESS_EXCEPTIONS 388 */ 389 @NonNull setSuppressExceptions(boolean suppress)390 public LaunchActivityBuilder setSuppressExceptions(boolean suppress) { 391 mSuppressExceptions = suppress; 392 return this; 393 } 394 395 /** 396 * Sets whether the launch should be performed with shell permissions. 397 * 398 * @param withShellPermission {@code true} if the launch should be performed with shell 399 * permissions, {@code false} otherwise. 400 * @return this LaunchActivityBuilder instance for chaining. 401 */ 402 @NonNull setWithShellPermission(boolean withShellPermission)403 public LaunchActivityBuilder setWithShellPermission(boolean withShellPermission) { 404 mWithShellPermission = withShellPermission; 405 return this; 406 } 407 408 /** 409 * Sets the activity session client to be used for launching the activity. 410 * 411 * @param sessionClient the activity session client, or {@code null} if not using a session 412 * client. 413 * @return this LaunchActivityBuilder instance for chaining. 414 */ 415 @NonNull setActivitySessionClient( @ullable CommandSession.ActivitySessionClient sessionClient)416 public LaunchActivityBuilder setActivitySessionClient( 417 @Nullable CommandSession.ActivitySessionClient sessionClient) { 418 mActivitySessionClient = sessionClient; 419 return this; 420 } 421 422 @Override shouldWaitForLaunched()423 public boolean shouldWaitForLaunched() { 424 return mWaitForLaunched; 425 } 426 427 /** 428 * Sets additional flags for the launch intent. 429 * 430 * @param flags the intent flags to be added. 431 * @return this LaunchActivityBuilder instance for chaining. 432 * @see ActivityLauncher#KEY_INTENT_FLAGS 433 */ 434 @NonNull setIntentFlags(int flags)435 public LaunchActivityBuilder setIntentFlags(int flags) { 436 mIntentFlags = flags; 437 return this; 438 } 439 440 /** 441 * Sets extras for the launch intent. 442 * 443 * @param extrasConsumer a consumer that will be called to populate the extras bundle, 444 * or {@code null} to do nothing. 445 * @return this LaunchActivityBuilder instance for chaining. 446 */ 447 @NonNull setIntentExtra(@ullable Consumer<Bundle> extrasConsumer)448 public LaunchActivityBuilder setIntentExtra(@Nullable Consumer<Bundle> extrasConsumer) { 449 if (extrasConsumer != null) { 450 mExtras = new Bundle(); 451 extrasConsumer.accept(mExtras); 452 } 453 return this; 454 } 455 456 @Override 457 @Nullable getExtras()458 public Bundle getExtras() { 459 return mExtras; 460 } 461 462 /** 463 * Sets the launch injector to be used for customizing the launch parameter. 464 * 465 * @param injector the launch injector to use, or {@code null} if no customization is needed. 466 * @see CommandSession.LaunchInjector 467 */ 468 @Override setLaunchInjector(@ullable CommandSession.LaunchInjector injector)469 public void setLaunchInjector(@Nullable CommandSession.LaunchInjector injector) { 470 mLaunchInjector = injector; 471 } 472 473 @Override execute()474 public void execute() { 475 if (mActivitySessionClient != null) { 476 final CommandSession.ActivitySessionClient client = mActivitySessionClient; 477 // Clear the session client so its startActivity can call the real execute(). 478 mActivitySessionClient = null; 479 client.startActivity(this); 480 return; 481 } 482 switch (mLauncherType) { 483 case INSTRUMENTATION: 484 if (mWithShellPermission) { 485 NestedShellPermission.run(this::launchUsingInstrumentation); 486 } else { 487 launchUsingInstrumentation(); 488 } 489 break; 490 case LAUNCHING_ACTIVITY: 491 case BROADCAST_RECEIVER: 492 launchUsingShellCommand(); 493 } 494 495 if (mWaitForLaunched) { 496 mAmWmState.waitForValidState(mTargetActivity); 497 } 498 } 499 500 /** Launches an activity using instrumentation. */ launchUsingInstrumentation()501 private void launchUsingInstrumentation() { 502 final Bundle b = new Bundle(); 503 b.putBoolean(KEY_LAUNCH_ACTIVITY, true); 504 b.putBoolean(KEY_LAUNCH_TO_SIDE, mToSide); 505 b.putBoolean(KEY_RANDOM_DATA, mRandomData); 506 b.putBoolean(KEY_NEW_TASK, mNewTask); 507 b.putBoolean(KEY_MULTIPLE_TASK, mMultipleTask); 508 b.putBoolean(KEY_MULTIPLE_INSTANCES, mAllowMultipleInstances); 509 b.putBoolean(KEY_LAUNCH_TASK_BEHIND, mLaunchTaskBehind); 510 b.putBoolean(KEY_REORDER_TO_FRONT, mReorderToFront); 511 b.putInt(KEY_DISPLAY_ID, mDisplayId); 512 b.putInt(KEY_WINDOWING_MODE, mWindowingMode); 513 b.putInt(KEY_ACTIVITY_TYPE, mActivityType); 514 b.putBoolean(KEY_USE_APPLICATION_CONTEXT, mUseApplicationContext); 515 b.putString(KEY_TARGET_COMPONENT, getActivityName(mTargetActivity)); 516 b.putBoolean(KEY_SUPPRESS_EXCEPTIONS, mSuppressExceptions); 517 b.putInt(KEY_INTENT_FLAGS, mIntentFlags); 518 b.putBundle(KEY_INTENT_EXTRAS, getExtras()); 519 b.putInt(KEY_TASK_DISPLAY_AREA_FEATURE_ID, mLaunchTaskDisplayAreaFeatureId); 520 final Context context = getInstrumentation().getContext(); 521 launchActivityFromExtras(context, b, mLaunchInjector); 522 } 523 524 /** Builds and executes a shell command to launch an activity. */ launchUsingShellCommand()525 private void launchUsingShellCommand() { 526 final StringBuilder commandBuilder = new StringBuilder(); 527 if (mBroadcastReceiver != null && mBroadcastReceiverAction != null) { 528 // Use broadcast receiver to launch the target. 529 commandBuilder.append("am broadcast -a ").append(mBroadcastReceiverAction) 530 .append(" -p ").append(mBroadcastReceiver.getPackageName()) 531 // Include stopped packages 532 .append(" -f 0x00000020"); 533 } else { 534 // If new task flag isn't set the windowing mode of launcher activity will be the 535 // windowing mode of the target activity, so we need to launch launcher activity in 536 // it. 537 String amStartCmd = 538 (mWindowingMode == -1 || mNewTask) 539 ? ActivityManagerTestBase.getAmStartCmd(mLaunchingActivity) 540 : ActivityManagerTestBase.getAmStartCmd(mLaunchingActivity, mDisplayId) 541 + " --windowingMode " + mWindowingMode; 542 // Use launching activity to launch the target. 543 commandBuilder.append(amStartCmd) 544 .append(" -f 0x20000020"); 545 } 546 547 // Add user for which activity needs to be started 548 commandBuilder.append(" --user ").append(Process.myUserHandle().getIdentifier()); 549 550 // Add a flag to ensure we actually mean to launch an activity. 551 commandBuilder.append(" --ez " + KEY_LAUNCH_ACTIVITY + " true"); 552 553 if (mToSide) { 554 commandBuilder.append(" --ez " + KEY_LAUNCH_TO_SIDE + " true"); 555 } 556 if (mRandomData) { 557 commandBuilder.append(" --ez " + KEY_RANDOM_DATA + " true"); 558 } 559 if (mNewTask) { 560 commandBuilder.append(" --ez " + KEY_NEW_TASK + " true"); 561 } 562 if (mMultipleTask) { 563 commandBuilder.append(" --ez " + KEY_MULTIPLE_TASK + " true"); 564 } 565 if (mAllowMultipleInstances) { 566 commandBuilder.append(" --ez " + KEY_MULTIPLE_INSTANCES + " true"); 567 } 568 if (mReorderToFront) { 569 commandBuilder.append(" --ez " + KEY_REORDER_TO_FRONT + " true"); 570 } 571 if (mFinishBeforeLaunch) { 572 commandBuilder.append(" --ez " + KEY_FINISH_BEFORE_LAUNCH + " true"); 573 } 574 if (mDisplayId != INVALID_DISPLAY) { 575 commandBuilder.append(" --ei " + KEY_DISPLAY_ID + " ").append(mDisplayId); 576 } 577 if (mWindowingMode != -1) { 578 commandBuilder.append(" --ei " + KEY_WINDOWING_MODE + " ").append(mWindowingMode); 579 } 580 if (mActivityType != ACTIVITY_TYPE_UNDEFINED) { 581 commandBuilder.append(" --ei " + KEY_ACTIVITY_TYPE + " ").append(mActivityType); 582 } 583 584 if (mUseApplicationContext) { 585 commandBuilder.append(" --ez " + KEY_USE_APPLICATION_CONTEXT + " true"); 586 } 587 588 if (mTargetActivity != null) { 589 // {@link ActivityLauncher} parses this extra string by 590 // {@link ComponentName#unflattenFromString(String)}. 591 commandBuilder.append(" --es " + KEY_TARGET_COMPONENT + " ") 592 .append(getActivityName(mTargetActivity)); 593 } 594 595 if (mSuppressExceptions) { 596 commandBuilder.append(" --ez " + KEY_SUPPRESS_EXCEPTIONS + " true"); 597 } 598 599 if (mIntentFlags != 0) { 600 commandBuilder.append(" --ei " + KEY_INTENT_FLAGS + " ").append(mIntentFlags); 601 } 602 603 if (mLaunchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) { 604 commandBuilder.append(" --task-display-area-feature-id ") 605 .append(mLaunchTaskDisplayAreaFeatureId); 606 commandBuilder.append(" --ei " + KEY_TASK_DISPLAY_AREA_FEATURE_ID + " ") 607 .append(mLaunchTaskDisplayAreaFeatureId); 608 } 609 610 if (mLaunchInjector != null) { 611 commandBuilder.append(" --ez " + KEY_FORWARD + " true"); 612 mLaunchInjector.setupShellCommand(commandBuilder); 613 } 614 executeShellCommand(commandBuilder.toString()); 615 } 616 } 617