• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
20 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
21 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
22 import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
23 import static android.server.wm.app.Components.TEST_ACTIVITY;
24 
25 import android.app.ActivityManager;
26 import android.app.ActivityOptions;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.server.wm.CommandSession.LaunchInjector;
33 import android.server.wm.TestJournalProvider.TestJournalContainer;
34 import android.text.TextUtils;
35 import android.util.Log;
36 
37 /** Utility class which contains common code for launching activities. */
38 public class ActivityLauncher {
39     public static final String TAG = ActivityLauncher.class.getSimpleName();
40 
41     /** Key for boolean extra, indicates whether it should launch an activity. */
42     public static final String KEY_LAUNCH_ACTIVITY = "launch_activity";
43     /**
44      * Key for boolean extra, indicates whether it the activity should be launched to side in
45      * split-screen.
46      */
47     public static final String KEY_LAUNCH_TO_SIDE = "launch_to_the_side";
48     /**
49      * Key for boolean extra, indicates if launch intent should include random data to be different
50      * from other launch intents.
51      */
52     public static final String KEY_RANDOM_DATA = "random_data";
53     /**
54      * Key for boolean extra, indicates if launch intent should have
55      * {@link Intent#FLAG_ACTIVITY_NEW_TASK}.
56      */
57     public static final String KEY_NEW_TASK = "new_task";
58     /**
59      * Key for boolean extra, indicates if launch intent should have
60      * {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}.
61      */
62     public static final String KEY_MULTIPLE_TASK = "multiple_task";
63     /**
64      * Key for boolean extra, indicates if launch intent should have
65      * {@link Intent#FLAG_ACTIVITY_REORDER_TO_FRONT}.
66      */
67     public static final String KEY_REORDER_TO_FRONT = "reorder_to_front";
68     /**
69      * Key for boolean extra, indicates if launch task without presented to user.
70      * {@link ActivityOptions#makeTaskLaunchBehind()}.
71      */
72     public static final String KEY_LAUNCH_TASK_BEHIND = "launch_task_behind";
73     /**
74      * Key for string extra with string representation of target component.
75      */
76     public static final String KEY_TARGET_COMPONENT = "target_component";
77     /**
78      * Key for int extra with target display id where the activity should be launched. Adding this
79      * automatically applies {@link Intent#FLAG_ACTIVITY_NEW_TASK} and
80      * {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK} to the intent.
81      */
82     public static final String KEY_DISPLAY_ID = "display_id";
83     /**
84      * Key for boolean extra, indicates if launch should be done from application context of the one
85      * passed in {@link #launchActivityFromExtras(Context, Bundle)}.
86      */
87     public static final String KEY_USE_APPLICATION_CONTEXT = "use_application_context";
88     /**
89      * Key for boolean extra, indicates if any exceptions thrown during launch other then
90      * {@link SecurityException} should be suppressed. A {@link SecurityException} is never thrown,
91      * it's always written to logs.
92      */
93     public static final String KEY_SUPPRESS_EXCEPTIONS = "suppress_exceptions";
94     /**
95      * Key for boolean extra, indicates the result of
96      * {@link ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
97      */
98     public static final String KEY_IS_ACTIVITY_START_ALLOWED_ON_DISPLAY =
99             "is_activity_start_allowed_on_display";
100     /**
101      * Key for boolean extra, indicates a security exception is caught when launching activity by
102      * {@link #launchActivityFromExtras}.
103      */
104     private static final String KEY_CAUGHT_SECURITY_EXCEPTION = "caught_security_exception";
105     /**
106      * Key for int extra with target activity type where activity should be launched as.
107      */
108     public static final String KEY_ACTIVITY_TYPE = "activity_type";
109     /**
110      * Key for int extra with intent flags which are used for launching an activity.
111      */
112     public static final String KEY_INTENT_FLAGS = "intent_flags";
113     /**
114      * Key for boolean extra, indicates if need to automatically applies
115      * {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK} to
116      * the intent when target display id set.
117      */
118     public static final String KEY_MULTIPLE_INSTANCES = "multiple_instances";
119 
120     /**
121      * Key for bundle extra to the intent which are used for launching an activity.
122      */
123     public static final String KEY_INTENT_EXTRAS = "intent_extras";
124 
125     /**
126      * Key for int extra, indicates the requested windowing mode.
127      */
128     public static final String KEY_WINDOWING_MODE = "windowing_mode";
129 
130 
131     /** Perform an activity launch configured by provided extras. */
launchActivityFromExtras(final Context context, Bundle extras)132     public static void launchActivityFromExtras(final Context context, Bundle extras) {
133         launchActivityFromExtras(context, extras, null /* launchInjector */);
134     }
135 
launchActivityFromExtras(final Context context, Bundle extras, LaunchInjector launchInjector)136     public static void launchActivityFromExtras(final Context context, Bundle extras,
137             LaunchInjector launchInjector) {
138         if (extras == null || !extras.getBoolean(KEY_LAUNCH_ACTIVITY)) {
139             return;
140         }
141 
142         Log.i(TAG, "launchActivityFromExtras: extras=" + extras);
143 
144         final String targetComponent = extras.getString(KEY_TARGET_COMPONENT);
145         final Intent newIntent = new Intent().setComponent(TextUtils.isEmpty(targetComponent)
146                 ? TEST_ACTIVITY : ComponentName.unflattenFromString(targetComponent));
147 
148         if (extras.getBoolean(KEY_LAUNCH_TO_SIDE)) {
149             newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_LAUNCH_ADJACENT);
150             if (extras.getBoolean(KEY_RANDOM_DATA)) {
151                 final Uri data = new Uri.Builder()
152                         .path(String.valueOf(System.currentTimeMillis()))
153                         .build();
154                 newIntent.setData(data);
155             }
156         }
157         if (extras.getBoolean(KEY_MULTIPLE_TASK)) {
158             newIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
159         }
160         if (extras.getBoolean(KEY_NEW_TASK)) {
161             newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
162         }
163 
164         if (extras.getBoolean(KEY_REORDER_TO_FRONT)) {
165             newIntent.addFlags(FLAG_ACTIVITY_REORDER_TO_FRONT);
166         }
167 
168         final Bundle intentExtras = extras.getBundle(KEY_INTENT_EXTRAS) ;
169         if (intentExtras != null) {
170             newIntent.putExtras(intentExtras);
171         }
172 
173         ActivityOptions options = extras.getBoolean(KEY_LAUNCH_TASK_BEHIND)
174                 ? ActivityOptions.makeTaskLaunchBehind() : null;
175         final int displayId = extras.getInt(KEY_DISPLAY_ID, -1);
176         if (displayId != -1) {
177             if (options == null) {
178                 options = ActivityOptions.makeBasic();
179             }
180             options.setLaunchDisplayId(displayId);
181             if (extras.getBoolean(KEY_MULTIPLE_INSTANCES)) {
182                 newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
183             }
184         }
185         final int windowingMode = extras.getInt(KEY_WINDOWING_MODE, -1);
186         if (windowingMode != -1) {
187             if (options == null) {
188                 options = ActivityOptions.makeBasic();
189             }
190             options.setLaunchWindowingMode(windowingMode);
191         }
192         if (launchInjector != null) {
193             launchInjector.setupIntent(newIntent);
194         }
195         final int activityType = extras.getInt(KEY_ACTIVITY_TYPE, -1);
196         if (activityType != -1) {
197             if (options == null) {
198                 options = ActivityOptions.makeBasic();
199             }
200             options.setLaunchActivityType(activityType);
201         }
202         final int intentFlags = extras.getInt(KEY_INTENT_FLAGS); // 0 if key doesn't exist.
203         if (intentFlags != 0) {
204             newIntent.addFlags(intentFlags);
205         }
206         final Bundle optionsBundle = options != null ? options.toBundle() : null;
207 
208         final Context launchContext = extras.getBoolean(KEY_USE_APPLICATION_CONTEXT) ?
209                 context.getApplicationContext() : context;
210 
211         try {
212             launchContext.startActivity(newIntent, optionsBundle);
213         } catch (SecurityException e) {
214             handleSecurityException(context, e);
215         } catch (Exception e) {
216             if (extras.getBoolean(KEY_SUPPRESS_EXCEPTIONS)) {
217                 Log.e(TAG, "Exception launching activity");
218             } else {
219                 throw e;
220             }
221         }
222     }
223 
checkActivityStartOnDisplay(Context context, int displayId, ComponentName componentName)224     public static void checkActivityStartOnDisplay(Context context, int displayId,
225             ComponentName componentName) {
226         final Intent launchIntent = new Intent(Intent.ACTION_VIEW).setComponent(componentName);
227 
228         final boolean isAllowed = context.getSystemService(ActivityManager.class)
229                 .isActivityStartAllowedOnDisplay(context, displayId, launchIntent);
230         Log.i(TAG, "isActivityStartAllowedOnDisplay=" + isAllowed);
231         TestJournalProvider.putExtras(context, TAG, bundle -> {
232             bundle.putBoolean(KEY_IS_ACTIVITY_START_ALLOWED_ON_DISPLAY, isAllowed);
233         });
234     }
235 
handleSecurityException(Context context, Exception e)236     public static void handleSecurityException(Context context, Exception e) {
237         Log.e(TAG, "SecurityException launching activity: " + e);
238         TestJournalProvider.putExtras(context, TAG, bundle -> {
239             bundle.putBoolean(KEY_CAUGHT_SECURITY_EXCEPTION, true);
240         });
241     }
242 
hasCaughtSecurityException()243     static boolean hasCaughtSecurityException() {
244         return TestJournalContainer.get(TAG).extras.containsKey(KEY_CAUGHT_SECURITY_EXCEPTION);
245     }
246 }
247