blob: 7a0afa2036f695757ab5970a2f14e5acb4cc72da [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;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000023import android.app.Activity;
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070024import android.app.ActivityTaskManager;
Jeff Sharkey97978802014-10-14 10:48:18 -070025import android.app.ActivityThread;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000026import android.app.AppGlobals;
Jeff Sharkey97978802014-10-14 10:48:18 -070027import android.app.admin.DevicePolicyManager;
Artur Satayeved5a6ae2019-12-10 17:47:54 +000028import android.compat.annotation.UnsupportedAppUsage;
arangelov590fba32020-02-11 18:05:42 +000029import android.content.ContentResolver;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000030import android.content.Intent;
arangelov64439c1e2018-07-31 14:18:47 +010031import android.content.pm.ActivityInfo;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000032import android.content.pm.IPackageManager;
Tony Mak96d78f52017-05-03 18:53:21 +010033import android.content.pm.PackageManager;
arangelov64439c1e2018-07-31 14:18:47 +010034import android.content.pm.ResolveInfo;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000035import android.content.pm.UserInfo;
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050036import android.metrics.LogMaker;
Jeff Sharkey97978802014-10-14 10:48:18 -070037import android.os.Bundle;
Robin Lee3ea43102014-07-23 23:41:31 +010038import android.os.RemoteException;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000039import android.os.UserHandle;
40import android.os.UserManager;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000041import android.util.Slog;
Robin Lee3ea43102014-07-23 23:41:31 +010042import android.widget.Toast;
Alison Cichowlas3e340502018-08-07 17:15:01 -040043
Tony Mak96d78f52017-05-03 18:53:21 +010044import com.android.internal.annotations.VisibleForTesting;
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050045import com.android.internal.logging.MetricsLogger;
46import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Alison Cichowlas3e340502018-08-07 17:15:01 -040047
arangelov64439c1e2018-07-31 14:18:47 +010048import java.util.Arrays;
49import java.util.HashSet;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000050import java.util.List;
arangelov64439c1e2018-07-31 14:18:47 +010051import java.util.Set;
Tony Mak96d78f52017-05-03 18:53:21 +010052
Jeff Sharkey97978802014-10-14 10:48:18 -070053/**
54 * This is used in conjunction with
55 * {@link DevicePolicyManager#addCrossProfileIntentFilter} to enable intents to
56 * be passed in and out of a managed profile.
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000057 */
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000058public class IntentForwarderActivity extends Activity {
Andrei Onea15884392019-03-22 17:28:11 +000059 @UnsupportedAppUsage
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000060 public static String TAG = "IntentForwarderActivity";
61
Nicolas Prevot741abfc2015-08-11 12:03:51 +010062 public static String FORWARD_INTENT_TO_PARENT
63 = "com.android.internal.app.ForwardIntentToParent";
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000064
65 public static String FORWARD_INTENT_TO_MANAGED_PROFILE
66 = "com.android.internal.app.ForwardIntentToManagedProfile";
67
arangelov38303742018-08-14 20:14:12 +010068 private static final Set<String> ALLOWED_TEXT_MESSAGE_SCHEMES
arangelov64439c1e2018-07-31 14:18:47 +010069 = new HashSet<>(Arrays.asList("sms", "smsto", "mms", "mmsto"));
70
arangelov38303742018-08-14 20:14:12 +010071 private static final String TEL_SCHEME = "tel";
72
Tony Mak96d78f52017-05-03 18:53:21 +010073 private Injector mInjector;
74
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050075 private MetricsLogger mMetricsLogger;
76
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000077 @Override
78 protected void onCreate(Bundle savedInstanceState) {
79 super.onCreate(savedInstanceState);
Tony Mak96d78f52017-05-03 18:53:21 +010080 mInjector = createInjector();
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000081
Tony Mak96d78f52017-05-03 18:53:21 +010082 Intent intentReceived = getIntent();
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000083 String className = intentReceived.getComponent().getClassName();
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +000084 final int targetUserId;
Robin Lee3ea43102014-07-23 23:41:31 +010085 final int userMessageId;
Nicolas Prevot741abfc2015-08-11 12:03:51 +010086 if (className.equals(FORWARD_INTENT_TO_PARENT)) {
Robin Lee3ea43102014-07-23 23:41:31 +010087 userMessageId = com.android.internal.R.string.forward_intent_to_owner;
Nicolas Prevot741abfc2015-08-11 12:03:51 +010088 targetUserId = getProfileParent();
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050089
90 getMetricsLogger().write(
91 new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
92 .setSubtype(MetricsEvent.PARENT_PROFILE));
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000093 } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
Robin Lee3ea43102014-07-23 23:41:31 +010094 userMessageId = com.android.internal.R.string.forward_intent_to_work;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +000095 targetUserId = getManagedProfile();
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050096
97 getMetricsLogger().write(
98 new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
99 .setSubtype(MetricsEvent.MANAGED_PROFILE));
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000100 } else {
101 Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
Robin Lee3ea43102014-07-23 23:41:31 +0100102 userMessageId = -1;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000103 targetUserId = UserHandle.USER_NULL;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000104 }
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000105 if (targetUserId == UserHandle.USER_NULL) {
Nicolas Prevot741abfc2015-08-11 12:03:51 +0100106 // This covers the case where there is no parent / managed profile.
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000107 finish();
108 return;
109 }
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000110
Tony Mak96d78f52017-05-03 18:53:21 +0100111 final int callingUserId = getUserId();
arangelov590fba32020-02-11 18:05:42 +0000112 final Intent newIntent = canForward(intentReceived, getUserId(), targetUserId,
113 mInjector.getIPackageManager(), getContentResolver());
Tony Mak96d78f52017-05-03 18:53:21 +0100114 if (newIntent != null) {
Nicolas Prevotdecd9ea2016-02-03 16:18:03 +0000115 if (Intent.ACTION_CHOOSER.equals(newIntent.getAction())) {
Tony Mak96d78f52017-05-03 18:53:21 +0100116 Intent innerIntent = newIntent.getParcelableExtra(Intent.EXTRA_INTENT);
Nicolas Prevotdecd9ea2016-02-03 16:18:03 +0000117 // At this point, innerIntent is not null. Otherwise, canForward would have returned
118 // false.
Nicolas Prevot107f7b72015-07-01 16:31:48 +0100119 innerIntent.prepareToLeaveUser(callingUserId);
Matt Pietal51ea0d62019-04-02 10:10:22 -0400120 innerIntent.fixUris(callingUserId);
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000121 } else {
Nicolas Prevot107f7b72015-07-01 16:31:48 +0100122 newIntent.prepareToLeaveUser(callingUserId);
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000123 }
Robin Lee3ea43102014-07-23 23:41:31 +0100124
arangelov64439c1e2018-07-31 14:18:47 +0100125 final ResolveInfo ri = mInjector.resolveActivityAsUser(newIntent, MATCH_DEFAULT_ONLY,
126 targetUserId);
Jeff Sharkey97978802014-10-14 10:48:18 -0700127 try {
Alison Cichowlas3e340502018-08-07 17:15:01 -0400128 startActivityAsCaller(newIntent, null, null, false, targetUserId);
Jeff Sharkey97978802014-10-14 10:48:18 -0700129 } catch (RuntimeException e) {
130 int launchedFromUid = -1;
131 String launchedFromPackage = "?";
132 try {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700133 launchedFromUid = ActivityTaskManager.getService().getLaunchedFromUid(
Jeff Sharkey97978802014-10-14 10:48:18 -0700134 getActivityToken());
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700135 launchedFromPackage = ActivityTaskManager.getService().getLaunchedFromPackage(
Jeff Sharkey97978802014-10-14 10:48:18 -0700136 getActivityToken());
137 } catch (RemoteException ignored) {
138 }
139
140 Slog.wtf(TAG, "Unable to launch as UID " + launchedFromUid + " package "
141 + launchedFromPackage + ", while running in "
142 + ActivityThread.currentProcessName(), e);
143 }
Robin Lee3ea43102014-07-23 23:41:31 +0100144
arangelov64439c1e2018-07-31 14:18:47 +0100145 if (shouldShowDisclosure(ri, intentReceived)) {
146 mInjector.showToast(userMessageId, Toast.LENGTH_LONG);
Robin Lee3ea43102014-07-23 23:41:31 +0100147 }
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000148 } else {
Tony Mak96d78f52017-05-03 18:53:21 +0100149 Slog.wtf(TAG, "the intent: " + intentReceived + " cannot be forwarded from user "
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000150 + callingUserId + " to user " + targetUserId);
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000151 }
152 finish();
153 }
154
arangelov64439c1e2018-07-31 14:18:47 +0100155 private boolean shouldShowDisclosure(@Nullable ResolveInfo ri, Intent intent) {
156 if (ri == null || ri.activityInfo == null) {
157 return true;
158 }
159 if (ri.activityInfo.applicationInfo.isSystemApp()
160 && (isDialerIntent(intent) || isTextMessageIntent(intent))) {
161 return false;
162 }
163 return !isTargetResolverOrChooserActivity(ri.activityInfo);
164 }
165
166 private boolean isTextMessageIntent(Intent intent) {
arangelov38303742018-08-14 20:14:12 +0100167 return (Intent.ACTION_SENDTO.equals(intent.getAction()) || isViewActionIntent(intent))
168 && ALLOWED_TEXT_MESSAGE_SCHEMES.contains(intent.getScheme());
arangelov64439c1e2018-07-31 14:18:47 +0100169 }
170
171 private boolean isDialerIntent(Intent intent) {
172 return Intent.ACTION_DIAL.equals(intent.getAction())
arangelov38303742018-08-14 20:14:12 +0100173 || Intent.ACTION_CALL.equals(intent.getAction())
174 || Intent.ACTION_CALL_PRIVILEGED.equals(intent.getAction())
175 || Intent.ACTION_CALL_EMERGENCY.equals(intent.getAction())
176 || (isViewActionIntent(intent) && TEL_SCHEME.equals(intent.getScheme()));
177 }
178
179 private boolean isViewActionIntent(Intent intent) {
180 return Intent.ACTION_VIEW.equals(intent.getAction())
181 && intent.hasCategory(Intent.CATEGORY_BROWSABLE);
arangelov64439c1e2018-07-31 14:18:47 +0100182 }
183
184 private boolean isTargetResolverOrChooserActivity(ActivityInfo activityInfo) {
185 if (!"android".equals(activityInfo.packageName)) {
186 return false;
187 }
188 return ResolverActivity.class.getName().equals(activityInfo.name)
189 || ChooserActivity.class.getName().equals(activityInfo.name);
190 }
191
Tony Mak96d78f52017-05-03 18:53:21 +0100192 /**
193 * Check whether the intent can be forwarded to target user. Return the intent used for
194 * forwarding if it can be forwarded, {@code null} otherwise.
195 */
arangelov590fba32020-02-11 18:05:42 +0000196 static Intent canForward(Intent incomingIntent, int sourceUserId, int targetUserId,
197 IPackageManager packageManager, ContentResolver contentResolver) {
Tony Mak96d78f52017-05-03 18:53:21 +0100198 Intent forwardIntent = new Intent(incomingIntent);
199 forwardIntent.addFlags(
200 Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
201 sanitizeIntent(forwardIntent);
202
203 Intent intentToCheck = forwardIntent;
204 if (Intent.ACTION_CHOOSER.equals(forwardIntent.getAction())) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000205 // The EXTRA_INITIAL_INTENTS may not be allowed to be forwarded.
Tony Mak96d78f52017-05-03 18:53:21 +0100206 if (forwardIntent.hasExtra(Intent.EXTRA_INITIAL_INTENTS)) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000207 Slog.wtf(TAG, "An chooser intent with extra initial intents cannot be forwarded to"
208 + " a different user");
Tony Mak96d78f52017-05-03 18:53:21 +0100209 return null;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000210 }
Tony Mak96d78f52017-05-03 18:53:21 +0100211 if (forwardIntent.hasExtra(Intent.EXTRA_REPLACEMENT_EXTRAS)) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000212 Slog.wtf(TAG, "A chooser intent with replacement extras cannot be forwarded to a"
213 + " different user");
Tony Mak96d78f52017-05-03 18:53:21 +0100214 return null;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000215 }
Tony Mak96d78f52017-05-03 18:53:21 +0100216 intentToCheck = forwardIntent.getParcelableExtra(Intent.EXTRA_INTENT);
217 if (intentToCheck == null) {
Nicolas Prevotdecd9ea2016-02-03 16:18:03 +0000218 Slog.wtf(TAG, "Cannot forward a chooser intent with no extra "
219 + Intent.EXTRA_INTENT);
Tony Mak96d78f52017-05-03 18:53:21 +0100220 return null;
Nicolas Prevotdecd9ea2016-02-03 16:18:03 +0000221 }
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000222 }
Tony Mak96d78f52017-05-03 18:53:21 +0100223 if (forwardIntent.getSelector() != null) {
224 intentToCheck = forwardIntent.getSelector();
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000225 }
arangelov590fba32020-02-11 18:05:42 +0000226 String resolvedType = intentToCheck.resolveTypeIfNeeded(contentResolver);
Tony Mak96d78f52017-05-03 18:53:21 +0100227 sanitizeIntent(intentToCheck);
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000228 try {
arangelov590fba32020-02-11 18:05:42 +0000229 if (packageManager.canForwardTo(
230 intentToCheck, resolvedType, sourceUserId, targetUserId)) {
Tony Mak96d78f52017-05-03 18:53:21 +0100231 return forwardIntent;
232 }
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000233 } catch (RemoteException e) {
234 Slog.e(TAG, "PackageManagerService is dead?");
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000235 }
Tony Mak96d78f52017-05-03 18:53:21 +0100236 return null;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000237 }
238
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000239 /**
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000240 * Returns the userId of the managed profile for this device or UserHandle.USER_NULL if there is
241 * no managed profile.
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000242 *
243 * TODO: Remove the assumption that there is only one managed profile
244 * on the device.
245 */
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000246 private int getManagedProfile() {
Tony Mak96d78f52017-05-03 18:53:21 +0100247 List<UserInfo> relatedUsers = mInjector.getUserManager().getProfiles(UserHandle.myUserId());
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000248 for (UserInfo userInfo : relatedUsers) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000249 if (userInfo.isManagedProfile()) return userInfo.id;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000250 }
251 Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE
252 + " has been called, but there is no managed profile");
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000253 return UserHandle.USER_NULL;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000254 }
Nicolas Prevot741abfc2015-08-11 12:03:51 +0100255
256 /**
257 * Returns the userId of the profile parent or UserHandle.USER_NULL if there is
258 * no parent.
259 */
260 private int getProfileParent() {
Tony Mak96d78f52017-05-03 18:53:21 +0100261 UserInfo parent = mInjector.getUserManager().getProfileParent(UserHandle.myUserId());
Nicolas Prevot741abfc2015-08-11 12:03:51 +0100262 if (parent == null) {
263 Slog.wtf(TAG, FORWARD_INTENT_TO_PARENT
264 + " has been called, but there is no parent");
265 return UserHandle.USER_NULL;
266 }
267 return parent.id;
268 }
Tony Mak96d78f52017-05-03 18:53:21 +0100269
270 /**
271 * Sanitize the intent in place.
272 */
arangelov590fba32020-02-11 18:05:42 +0000273 private static void sanitizeIntent(Intent intent) {
Tony Mak96d78f52017-05-03 18:53:21 +0100274 // Apps should not be allowed to target a specific package/ component in the target user.
275 intent.setPackage(null);
276 intent.setComponent(null);
277 }
278
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -0500279 protected MetricsLogger getMetricsLogger() {
280 if (mMetricsLogger == null) {
281 mMetricsLogger = new MetricsLogger();
282 }
283 return mMetricsLogger;
284 }
285
Tony Mak96d78f52017-05-03 18:53:21 +0100286 @VisibleForTesting
287 protected Injector createInjector() {
288 return new InjectorImpl();
289 }
290
291 private class InjectorImpl implements Injector {
292
293 @Override
294 public IPackageManager getIPackageManager() {
295 return AppGlobals.getPackageManager();
296 }
297
298 @Override
299 public UserManager getUserManager() {
300 return getSystemService(UserManager.class);
301 }
302
303 @Override
304 public PackageManager getPackageManager() {
305 return IntentForwarderActivity.this.getPackageManager();
306 }
arangelov64439c1e2018-07-31 14:18:47 +0100307
308 @Override
309 public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
310 return getPackageManager().resolveActivityAsUser(intent, flags, userId);
311 }
312
313 @Override
314 public void showToast(int messageId, int duration) {
315 Toast.makeText(IntentForwarderActivity.this, getString(messageId), duration).show();
316 }
Tony Mak96d78f52017-05-03 18:53:21 +0100317 }
318
319 public interface Injector {
320 IPackageManager getIPackageManager();
321
322 UserManager getUserManager();
323
324 PackageManager getPackageManager();
arangelov64439c1e2018-07-31 14:18:47 +0100325
326 ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId);
327
328 void showToast(@StringRes int messageId, int duration);
Tony Mak96d78f52017-05-03 18:53:21 +0100329 }
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000330}