xref: /aosp_15_r20/cts/tests/framework/base/windowmanager/util/src/android/server/wm/LaunchActivityBuilder.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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