blob: a5f055f095620fe2d3b192c8fd124a8399c76a2d [file] [log] [blame]
Nicolas Prevot10fa67c2014-03-24 13:44:38 +00001/*
2 * Copyright (C) 2014 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
17package com.android.internal.app;
18
arangelov64439c1e2018-07-31 14:18:47 +010019import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
20
21import android.annotation.Nullable;
22import android.annotation.StringRes;
Andrei Onea15884392019-03-22 17:28:11 +000023import android.annotation.UnsupportedAppUsage;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000024import android.app.Activity;
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070025import android.app.ActivityTaskManager;
Jeff Sharkey97978802014-10-14 10:48:18 -070026import android.app.ActivityThread;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000027import android.app.AppGlobals;
Jeff Sharkey97978802014-10-14 10:48:18 -070028import android.app.admin.DevicePolicyManager;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000029import android.content.Intent;
arangelov64439c1e2018-07-31 14:18:47 +010030import android.content.pm.ActivityInfo;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000031import android.content.pm.IPackageManager;
Tony Mak96d78f52017-05-03 18:53:21 +010032import android.content.pm.PackageManager;
arangelov64439c1e2018-07-31 14:18:47 +010033import android.content.pm.ResolveInfo;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000034import android.content.pm.UserInfo;
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050035import android.metrics.LogMaker;
Jeff Sharkey97978802014-10-14 10:48:18 -070036import android.os.Bundle;
Robin Lee3ea43102014-07-23 23:41:31 +010037import android.os.RemoteException;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000038import android.os.UserHandle;
39import android.os.UserManager;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000040import android.util.Slog;
Robin Lee3ea43102014-07-23 23:41:31 +010041import android.widget.Toast;
Alison Cichowlas3e340502018-08-07 17:15:01 -040042
Tony Mak96d78f52017-05-03 18:53:21 +010043import com.android.internal.annotations.VisibleForTesting;
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050044import com.android.internal.logging.MetricsLogger;
45import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Alison Cichowlas3e340502018-08-07 17:15:01 -040046
arangelov64439c1e2018-07-31 14:18:47 +010047import java.util.Arrays;
48import java.util.HashSet;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000049import java.util.List;
arangelov64439c1e2018-07-31 14:18:47 +010050import java.util.Set;
Tony Mak96d78f52017-05-03 18:53:21 +010051
Jeff Sharkey97978802014-10-14 10:48:18 -070052/**
53 * This is used in conjunction with
54 * {@link DevicePolicyManager#addCrossProfileIntentFilter} to enable intents to
55 * be passed in and out of a managed profile.
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000056 */
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000057public class IntentForwarderActivity extends Activity {
Andrei Onea15884392019-03-22 17:28:11 +000058 @UnsupportedAppUsage
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000059 public static String TAG = "IntentForwarderActivity";
60
Nicolas Prevot741abfc2015-08-11 12:03:51 +010061 public static String FORWARD_INTENT_TO_PARENT
62 = "com.android.internal.app.ForwardIntentToParent";
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000063
64 public static String FORWARD_INTENT_TO_MANAGED_PROFILE
65 = "com.android.internal.app.ForwardIntentToManagedProfile";
66
arangelov38303742018-08-14 20:14:12 +010067 private static final Set<String> ALLOWED_TEXT_MESSAGE_SCHEMES
arangelov64439c1e2018-07-31 14:18:47 +010068 = new HashSet<>(Arrays.asList("sms", "smsto", "mms", "mmsto"));
69
arangelov38303742018-08-14 20:14:12 +010070 private static final String TEL_SCHEME = "tel";
71
Tony Mak96d78f52017-05-03 18:53:21 +010072 private Injector mInjector;
73
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050074 private MetricsLogger mMetricsLogger;
75
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000076 @Override
77 protected void onCreate(Bundle savedInstanceState) {
78 super.onCreate(savedInstanceState);
Tony Mak96d78f52017-05-03 18:53:21 +010079 mInjector = createInjector();
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000080
Tony Mak96d78f52017-05-03 18:53:21 +010081 Intent intentReceived = getIntent();
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000082 String className = intentReceived.getComponent().getClassName();
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +000083 final int targetUserId;
Robin Lee3ea43102014-07-23 23:41:31 +010084 final int userMessageId;
Nicolas Prevot741abfc2015-08-11 12:03:51 +010085 if (className.equals(FORWARD_INTENT_TO_PARENT)) {
Robin Lee3ea43102014-07-23 23:41:31 +010086 userMessageId = com.android.internal.R.string.forward_intent_to_owner;
Nicolas Prevot741abfc2015-08-11 12:03:51 +010087 targetUserId = getProfileParent();
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050088
89 getMetricsLogger().write(
90 new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
91 .setSubtype(MetricsEvent.PARENT_PROFILE));
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000092 } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
Robin Lee3ea43102014-07-23 23:41:31 +010093 userMessageId = com.android.internal.R.string.forward_intent_to_work;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +000094 targetUserId = getManagedProfile();
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050095
96 getMetricsLogger().write(
97 new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
98 .setSubtype(MetricsEvent.MANAGED_PROFILE));
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000099 } else {
100 Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
Robin Lee3ea43102014-07-23 23:41:31 +0100101 userMessageId = -1;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000102 targetUserId = UserHandle.USER_NULL;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000103 }
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000104 if (targetUserId == UserHandle.USER_NULL) {
Nicolas Prevot741abfc2015-08-11 12:03:51 +0100105 // This covers the case where there is no parent / managed profile.
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000106 finish();
107 return;
108 }
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000109
Tony Mak96d78f52017-05-03 18:53:21 +0100110 final int callingUserId = getUserId();
111 final Intent newIntent = canForward(intentReceived, targetUserId);
112 if (newIntent != null) {
Nicolas Prevotdecd9ea2016-02-03 16:18:03 +0000113 if (Intent.ACTION_CHOOSER.equals(newIntent.getAction())) {
Tony Mak96d78f52017-05-03 18:53:21 +0100114 Intent innerIntent = newIntent.getParcelableExtra(Intent.EXTRA_INTENT);
Nicolas Prevotdecd9ea2016-02-03 16:18:03 +0000115 // At this point, innerIntent is not null. Otherwise, canForward would have returned
116 // false.
Nicolas Prevot107f7b72015-07-01 16:31:48 +0100117 innerIntent.prepareToLeaveUser(callingUserId);
Matt Pietal51ea0d62019-04-02 10:10:22 -0400118 innerIntent.fixUris(callingUserId);
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000119 } else {
Nicolas Prevot107f7b72015-07-01 16:31:48 +0100120 newIntent.prepareToLeaveUser(callingUserId);
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000121 }
Robin Lee3ea43102014-07-23 23:41:31 +0100122
arangelov64439c1e2018-07-31 14:18:47 +0100123 final ResolveInfo ri = mInjector.resolveActivityAsUser(newIntent, MATCH_DEFAULT_ONLY,
124 targetUserId);
Jeff Sharkey97978802014-10-14 10:48:18 -0700125 try {
Alison Cichowlas3e340502018-08-07 17:15:01 -0400126 startActivityAsCaller(newIntent, null, null, false, targetUserId);
Jeff Sharkey97978802014-10-14 10:48:18 -0700127 } catch (RuntimeException e) {
128 int launchedFromUid = -1;
129 String launchedFromPackage = "?";
130 try {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700131 launchedFromUid = ActivityTaskManager.getService().getLaunchedFromUid(
Jeff Sharkey97978802014-10-14 10:48:18 -0700132 getActivityToken());
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700133 launchedFromPackage = ActivityTaskManager.getService().getLaunchedFromPackage(
Jeff Sharkey97978802014-10-14 10:48:18 -0700134 getActivityToken());
135 } catch (RemoteException ignored) {
136 }
137
138 Slog.wtf(TAG, "Unable to launch as UID " + launchedFromUid + " package "
139 + launchedFromPackage + ", while running in "
140 + ActivityThread.currentProcessName(), e);
141 }
Robin Lee3ea43102014-07-23 23:41:31 +0100142
arangelov64439c1e2018-07-31 14:18:47 +0100143 if (shouldShowDisclosure(ri, intentReceived)) {
144 mInjector.showToast(userMessageId, Toast.LENGTH_LONG);
Robin Lee3ea43102014-07-23 23:41:31 +0100145 }
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000146 } else {
Tony Mak96d78f52017-05-03 18:53:21 +0100147 Slog.wtf(TAG, "the intent: " + intentReceived + " cannot be forwarded from user "
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000148 + callingUserId + " to user " + targetUserId);
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000149 }
150 finish();
151 }
152
arangelov64439c1e2018-07-31 14:18:47 +0100153 private boolean shouldShowDisclosure(@Nullable ResolveInfo ri, Intent intent) {
154 if (ri == null || ri.activityInfo == null) {
155 return true;
156 }
157 if (ri.activityInfo.applicationInfo.isSystemApp()
158 && (isDialerIntent(intent) || isTextMessageIntent(intent))) {
159 return false;
160 }
161 return !isTargetResolverOrChooserActivity(ri.activityInfo);
162 }
163
164 private boolean isTextMessageIntent(Intent intent) {
arangelov38303742018-08-14 20:14:12 +0100165 return (Intent.ACTION_SENDTO.equals(intent.getAction()) || isViewActionIntent(intent))
166 && ALLOWED_TEXT_MESSAGE_SCHEMES.contains(intent.getScheme());
arangelov64439c1e2018-07-31 14:18:47 +0100167 }
168
169 private boolean isDialerIntent(Intent intent) {
170 return Intent.ACTION_DIAL.equals(intent.getAction())
arangelov38303742018-08-14 20:14:12 +0100171 || Intent.ACTION_CALL.equals(intent.getAction())
172 || Intent.ACTION_CALL_PRIVILEGED.equals(intent.getAction())
173 || Intent.ACTION_CALL_EMERGENCY.equals(intent.getAction())
174 || (isViewActionIntent(intent) && TEL_SCHEME.equals(intent.getScheme()));
175 }
176
177 private boolean isViewActionIntent(Intent intent) {
178 return Intent.ACTION_VIEW.equals(intent.getAction())
179 && intent.hasCategory(Intent.CATEGORY_BROWSABLE);
arangelov64439c1e2018-07-31 14:18:47 +0100180 }
181
182 private boolean isTargetResolverOrChooserActivity(ActivityInfo activityInfo) {
183 if (!"android".equals(activityInfo.packageName)) {
184 return false;
185 }
186 return ResolverActivity.class.getName().equals(activityInfo.name)
187 || ChooserActivity.class.getName().equals(activityInfo.name);
188 }
189
Tony Mak96d78f52017-05-03 18:53:21 +0100190 /**
191 * Check whether the intent can be forwarded to target user. Return the intent used for
192 * forwarding if it can be forwarded, {@code null} otherwise.
193 */
194 Intent canForward(Intent incomingIntent, int targetUserId) {
195 Intent forwardIntent = new Intent(incomingIntent);
196 forwardIntent.addFlags(
197 Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
198 sanitizeIntent(forwardIntent);
199
200 Intent intentToCheck = forwardIntent;
201 if (Intent.ACTION_CHOOSER.equals(forwardIntent.getAction())) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000202 // The EXTRA_INITIAL_INTENTS may not be allowed to be forwarded.
Tony Mak96d78f52017-05-03 18:53:21 +0100203 if (forwardIntent.hasExtra(Intent.EXTRA_INITIAL_INTENTS)) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000204 Slog.wtf(TAG, "An chooser intent with extra initial intents cannot be forwarded to"
205 + " a different user");
Tony Mak96d78f52017-05-03 18:53:21 +0100206 return null;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000207 }
Tony Mak96d78f52017-05-03 18:53:21 +0100208 if (forwardIntent.hasExtra(Intent.EXTRA_REPLACEMENT_EXTRAS)) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000209 Slog.wtf(TAG, "A chooser intent with replacement extras cannot be forwarded to a"
210 + " different user");
Tony Mak96d78f52017-05-03 18:53:21 +0100211 return null;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000212 }
Tony Mak96d78f52017-05-03 18:53:21 +0100213 intentToCheck = forwardIntent.getParcelableExtra(Intent.EXTRA_INTENT);
214 if (intentToCheck == null) {
Nicolas Prevotdecd9ea2016-02-03 16:18:03 +0000215 Slog.wtf(TAG, "Cannot forward a chooser intent with no extra "
216 + Intent.EXTRA_INTENT);
Tony Mak96d78f52017-05-03 18:53:21 +0100217 return null;
Nicolas Prevotdecd9ea2016-02-03 16:18:03 +0000218 }
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000219 }
Tony Mak96d78f52017-05-03 18:53:21 +0100220 if (forwardIntent.getSelector() != null) {
221 intentToCheck = forwardIntent.getSelector();
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000222 }
Tony Mak96d78f52017-05-03 18:53:21 +0100223 String resolvedType = intentToCheck.resolveTypeIfNeeded(getContentResolver());
224 sanitizeIntent(intentToCheck);
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000225 try {
Tony Mak96d78f52017-05-03 18:53:21 +0100226 if (mInjector.getIPackageManager().
227 canForwardTo(intentToCheck, resolvedType, getUserId(), targetUserId)) {
228 return forwardIntent;
229 }
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000230 } catch (RemoteException e) {
231 Slog.e(TAG, "PackageManagerService is dead?");
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000232 }
Tony Mak96d78f52017-05-03 18:53:21 +0100233 return null;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000234 }
235
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000236 /**
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000237 * Returns the userId of the managed profile for this device or UserHandle.USER_NULL if there is
238 * no managed profile.
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000239 *
240 * TODO: Remove the assumption that there is only one managed profile
241 * on the device.
242 */
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000243 private int getManagedProfile() {
Tony Mak96d78f52017-05-03 18:53:21 +0100244 List<UserInfo> relatedUsers = mInjector.getUserManager().getProfiles(UserHandle.myUserId());
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000245 for (UserInfo userInfo : relatedUsers) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000246 if (userInfo.isManagedProfile()) return userInfo.id;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000247 }
248 Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE
249 + " has been called, but there is no managed profile");
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000250 return UserHandle.USER_NULL;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000251 }
Nicolas Prevot741abfc2015-08-11 12:03:51 +0100252
253 /**
254 * Returns the userId of the profile parent or UserHandle.USER_NULL if there is
255 * no parent.
256 */
257 private int getProfileParent() {
Tony Mak96d78f52017-05-03 18:53:21 +0100258 UserInfo parent = mInjector.getUserManager().getProfileParent(UserHandle.myUserId());
Nicolas Prevot741abfc2015-08-11 12:03:51 +0100259 if (parent == null) {
260 Slog.wtf(TAG, FORWARD_INTENT_TO_PARENT
261 + " has been called, but there is no parent");
262 return UserHandle.USER_NULL;
263 }
264 return parent.id;
265 }
Tony Mak96d78f52017-05-03 18:53:21 +0100266
267 /**
268 * Sanitize the intent in place.
269 */
270 private void sanitizeIntent(Intent intent) {
271 // Apps should not be allowed to target a specific package/ component in the target user.
272 intent.setPackage(null);
273 intent.setComponent(null);
274 }
275
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -0500276 protected MetricsLogger getMetricsLogger() {
277 if (mMetricsLogger == null) {
278 mMetricsLogger = new MetricsLogger();
279 }
280 return mMetricsLogger;
281 }
282
Tony Mak96d78f52017-05-03 18:53:21 +0100283 @VisibleForTesting
284 protected Injector createInjector() {
285 return new InjectorImpl();
286 }
287
288 private class InjectorImpl implements Injector {
289
290 @Override
291 public IPackageManager getIPackageManager() {
292 return AppGlobals.getPackageManager();
293 }
294
295 @Override
296 public UserManager getUserManager() {
297 return getSystemService(UserManager.class);
298 }
299
300 @Override
301 public PackageManager getPackageManager() {
302 return IntentForwarderActivity.this.getPackageManager();
303 }
arangelov64439c1e2018-07-31 14:18:47 +0100304
305 @Override
306 public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
307 return getPackageManager().resolveActivityAsUser(intent, flags, userId);
308 }
309
310 @Override
311 public void showToast(int messageId, int duration) {
312 Toast.makeText(IntentForwarderActivity.this, getString(messageId), duration).show();
313 }
Tony Mak96d78f52017-05-03 18:53:21 +0100314 }
315
316 public interface Injector {
317 IPackageManager getIPackageManager();
318
319 UserManager getUserManager();
320
321 PackageManager getPackageManager();
arangelov64439c1e2018-07-31 14:18:47 +0100322
323 ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId);
324
325 void showToast(@StringRes int messageId, int duration);
Tony Mak96d78f52017-05-03 18:53:21 +0100326 }
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000327}