blob: 9bdfa4ad4e43828a9a65a8ecceab37356f3fb4a8 [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;
Eric Sandnesse89d0e12020-03-13 17:55:51 +000041import android.provider.Settings;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000042import android.util.Slog;
Robin Lee3ea43102014-07-23 23:41:31 +010043import android.widget.Toast;
Alison Cichowlas3e340502018-08-07 17:15:01 -040044
Tony Mak96d78f52017-05-03 18:53:21 +010045import com.android.internal.annotations.VisibleForTesting;
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050046import com.android.internal.logging.MetricsLogger;
47import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Alison Cichowlas3e340502018-08-07 17:15:01 -040048
arangelov64439c1e2018-07-31 14:18:47 +010049import java.util.Arrays;
50import java.util.HashSet;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000051import java.util.List;
arangelov64439c1e2018-07-31 14:18:47 +010052import java.util.Set;
Tony Mak96d78f52017-05-03 18:53:21 +010053
Jeff Sharkey97978802014-10-14 10:48:18 -070054/**
55 * This is used in conjunction with
56 * {@link DevicePolicyManager#addCrossProfileIntentFilter} to enable intents to
57 * be passed in and out of a managed profile.
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000058 */
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000059public class IntentForwarderActivity extends Activity {
Andrei Onea15884392019-03-22 17:28:11 +000060 @UnsupportedAppUsage
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000061 public static String TAG = "IntentForwarderActivity";
62
Nicolas Prevot741abfc2015-08-11 12:03:51 +010063 public static String FORWARD_INTENT_TO_PARENT
64 = "com.android.internal.app.ForwardIntentToParent";
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000065
66 public static String FORWARD_INTENT_TO_MANAGED_PROFILE
67 = "com.android.internal.app.ForwardIntentToManagedProfile";
68
arangelov38303742018-08-14 20:14:12 +010069 private static final Set<String> ALLOWED_TEXT_MESSAGE_SCHEMES
arangelov64439c1e2018-07-31 14:18:47 +010070 = new HashSet<>(Arrays.asList("sms", "smsto", "mms", "mmsto"));
71
arangelov38303742018-08-14 20:14:12 +010072 private static final String TEL_SCHEME = "tel";
73
Tony Mak96d78f52017-05-03 18:53:21 +010074 private Injector mInjector;
75
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050076 private MetricsLogger mMetricsLogger;
77
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000078 @Override
79 protected void onCreate(Bundle savedInstanceState) {
80 super.onCreate(savedInstanceState);
Tony Mak96d78f52017-05-03 18:53:21 +010081 mInjector = createInjector();
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000082
Tony Mak96d78f52017-05-03 18:53:21 +010083 Intent intentReceived = getIntent();
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000084 String className = intentReceived.getComponent().getClassName();
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +000085 final int targetUserId;
Robin Lee3ea43102014-07-23 23:41:31 +010086 final int userMessageId;
Nicolas Prevot741abfc2015-08-11 12:03:51 +010087 if (className.equals(FORWARD_INTENT_TO_PARENT)) {
Robin Lee3ea43102014-07-23 23:41:31 +010088 userMessageId = com.android.internal.R.string.forward_intent_to_owner;
Nicolas Prevot741abfc2015-08-11 12:03:51 +010089 targetUserId = getProfileParent();
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050090
91 getMetricsLogger().write(
92 new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
93 .setSubtype(MetricsEvent.PARENT_PROFILE));
Nicolas Prevot10fa67c2014-03-24 13:44:38 +000094 } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
Robin Lee3ea43102014-07-23 23:41:31 +010095 userMessageId = com.android.internal.R.string.forward_intent_to_work;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +000096 targetUserId = getManagedProfile();
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -050097
98 getMetricsLogger().write(
99 new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
100 .setSubtype(MetricsEvent.MANAGED_PROFILE));
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000101 } else {
102 Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
Robin Lee3ea43102014-07-23 23:41:31 +0100103 userMessageId = -1;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000104 targetUserId = UserHandle.USER_NULL;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000105 }
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000106 if (targetUserId == UserHandle.USER_NULL) {
Nicolas Prevot741abfc2015-08-11 12:03:51 +0100107 // This covers the case where there is no parent / managed profile.
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000108 finish();
109 return;
110 }
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000111
Tony Mak96d78f52017-05-03 18:53:21 +0100112 final int callingUserId = getUserId();
arangelov590fba32020-02-11 18:05:42 +0000113 final Intent newIntent = canForward(intentReceived, getUserId(), targetUserId,
114 mInjector.getIPackageManager(), getContentResolver());
Tony Mak96d78f52017-05-03 18:53:21 +0100115 if (newIntent != null) {
Nicolas Prevotdecd9ea2016-02-03 16:18:03 +0000116 if (Intent.ACTION_CHOOSER.equals(newIntent.getAction())) {
Tony Mak96d78f52017-05-03 18:53:21 +0100117 Intent innerIntent = newIntent.getParcelableExtra(Intent.EXTRA_INTENT);
Nicolas Prevotdecd9ea2016-02-03 16:18:03 +0000118 // At this point, innerIntent is not null. Otherwise, canForward would have returned
119 // false.
Nicolas Prevot107f7b72015-07-01 16:31:48 +0100120 innerIntent.prepareToLeaveUser(callingUserId);
Matt Pietal51ea0d62019-04-02 10:10:22 -0400121 innerIntent.fixUris(callingUserId);
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000122 } else {
Nicolas Prevot107f7b72015-07-01 16:31:48 +0100123 newIntent.prepareToLeaveUser(callingUserId);
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000124 }
Robin Lee3ea43102014-07-23 23:41:31 +0100125
arangelov64439c1e2018-07-31 14:18:47 +0100126 final ResolveInfo ri = mInjector.resolveActivityAsUser(newIntent, MATCH_DEFAULT_ONLY,
127 targetUserId);
Jeff Sharkey97978802014-10-14 10:48:18 -0700128 try {
Alison Cichowlas3e340502018-08-07 17:15:01 -0400129 startActivityAsCaller(newIntent, null, null, false, targetUserId);
Jeff Sharkey97978802014-10-14 10:48:18 -0700130 } catch (RuntimeException e) {
131 int launchedFromUid = -1;
132 String launchedFromPackage = "?";
133 try {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700134 launchedFromUid = ActivityTaskManager.getService().getLaunchedFromUid(
Jeff Sharkey97978802014-10-14 10:48:18 -0700135 getActivityToken());
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700136 launchedFromPackage = ActivityTaskManager.getService().getLaunchedFromPackage(
Jeff Sharkey97978802014-10-14 10:48:18 -0700137 getActivityToken());
138 } catch (RemoteException ignored) {
139 }
140
141 Slog.wtf(TAG, "Unable to launch as UID " + launchedFromUid + " package "
142 + launchedFromPackage + ", while running in "
143 + ActivityThread.currentProcessName(), e);
144 }
Robin Lee3ea43102014-07-23 23:41:31 +0100145
arangelov64439c1e2018-07-31 14:18:47 +0100146 if (shouldShowDisclosure(ri, intentReceived)) {
147 mInjector.showToast(userMessageId, Toast.LENGTH_LONG);
Robin Lee3ea43102014-07-23 23:41:31 +0100148 }
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000149 } else {
Tony Mak96d78f52017-05-03 18:53:21 +0100150 Slog.wtf(TAG, "the intent: " + intentReceived + " cannot be forwarded from user "
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000151 + callingUserId + " to user " + targetUserId);
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000152 }
153 finish();
154 }
155
arangelov64439c1e2018-07-31 14:18:47 +0100156 private boolean shouldShowDisclosure(@Nullable ResolveInfo ri, Intent intent) {
Eric Sandnesse89d0e12020-03-13 17:55:51 +0000157 if (!isDeviceProvisioned()) {
158 return false;
159 }
arangelov64439c1e2018-07-31 14:18:47 +0100160 if (ri == null || ri.activityInfo == null) {
161 return true;
162 }
163 if (ri.activityInfo.applicationInfo.isSystemApp()
164 && (isDialerIntent(intent) || isTextMessageIntent(intent))) {
165 return false;
166 }
167 return !isTargetResolverOrChooserActivity(ri.activityInfo);
168 }
169
Eric Sandnesse89d0e12020-03-13 17:55:51 +0000170 private boolean isDeviceProvisioned() {
171 return Settings.Global.getInt(getContentResolver(),
172 Settings.Global.DEVICE_PROVISIONED, /* def= */ 0) != 0;
173 }
174
arangelov64439c1e2018-07-31 14:18:47 +0100175 private boolean isTextMessageIntent(Intent intent) {
arangelov38303742018-08-14 20:14:12 +0100176 return (Intent.ACTION_SENDTO.equals(intent.getAction()) || isViewActionIntent(intent))
177 && ALLOWED_TEXT_MESSAGE_SCHEMES.contains(intent.getScheme());
arangelov64439c1e2018-07-31 14:18:47 +0100178 }
179
180 private boolean isDialerIntent(Intent intent) {
181 return Intent.ACTION_DIAL.equals(intent.getAction())
arangelov38303742018-08-14 20:14:12 +0100182 || Intent.ACTION_CALL.equals(intent.getAction())
183 || Intent.ACTION_CALL_PRIVILEGED.equals(intent.getAction())
184 || Intent.ACTION_CALL_EMERGENCY.equals(intent.getAction())
185 || (isViewActionIntent(intent) && TEL_SCHEME.equals(intent.getScheme()));
186 }
187
188 private boolean isViewActionIntent(Intent intent) {
189 return Intent.ACTION_VIEW.equals(intent.getAction())
190 && intent.hasCategory(Intent.CATEGORY_BROWSABLE);
arangelov64439c1e2018-07-31 14:18:47 +0100191 }
192
193 private boolean isTargetResolverOrChooserActivity(ActivityInfo activityInfo) {
194 if (!"android".equals(activityInfo.packageName)) {
195 return false;
196 }
197 return ResolverActivity.class.getName().equals(activityInfo.name)
198 || ChooserActivity.class.getName().equals(activityInfo.name);
199 }
200
Tony Mak96d78f52017-05-03 18:53:21 +0100201 /**
202 * Check whether the intent can be forwarded to target user. Return the intent used for
203 * forwarding if it can be forwarded, {@code null} otherwise.
204 */
arangelov590fba32020-02-11 18:05:42 +0000205 static Intent canForward(Intent incomingIntent, int sourceUserId, int targetUserId,
206 IPackageManager packageManager, ContentResolver contentResolver) {
Tony Mak96d78f52017-05-03 18:53:21 +0100207 Intent forwardIntent = new Intent(incomingIntent);
208 forwardIntent.addFlags(
209 Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
210 sanitizeIntent(forwardIntent);
211
212 Intent intentToCheck = forwardIntent;
213 if (Intent.ACTION_CHOOSER.equals(forwardIntent.getAction())) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000214 // The EXTRA_INITIAL_INTENTS may not be allowed to be forwarded.
Tony Mak96d78f52017-05-03 18:53:21 +0100215 if (forwardIntent.hasExtra(Intent.EXTRA_INITIAL_INTENTS)) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000216 Slog.wtf(TAG, "An chooser intent with extra initial intents cannot be forwarded to"
217 + " a different user");
Tony Mak96d78f52017-05-03 18:53:21 +0100218 return null;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000219 }
Tony Mak96d78f52017-05-03 18:53:21 +0100220 if (forwardIntent.hasExtra(Intent.EXTRA_REPLACEMENT_EXTRAS)) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000221 Slog.wtf(TAG, "A chooser intent with replacement extras cannot be forwarded to a"
222 + " different user");
Tony Mak96d78f52017-05-03 18:53:21 +0100223 return null;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000224 }
Tony Mak96d78f52017-05-03 18:53:21 +0100225 intentToCheck = forwardIntent.getParcelableExtra(Intent.EXTRA_INTENT);
226 if (intentToCheck == null) {
Nicolas Prevotdecd9ea2016-02-03 16:18:03 +0000227 Slog.wtf(TAG, "Cannot forward a chooser intent with no extra "
228 + Intent.EXTRA_INTENT);
Tony Mak96d78f52017-05-03 18:53:21 +0100229 return null;
Nicolas Prevotdecd9ea2016-02-03 16:18:03 +0000230 }
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000231 }
Tony Mak96d78f52017-05-03 18:53:21 +0100232 if (forwardIntent.getSelector() != null) {
233 intentToCheck = forwardIntent.getSelector();
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000234 }
arangelov590fba32020-02-11 18:05:42 +0000235 String resolvedType = intentToCheck.resolveTypeIfNeeded(contentResolver);
Tony Mak96d78f52017-05-03 18:53:21 +0100236 sanitizeIntent(intentToCheck);
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000237 try {
arangelov590fba32020-02-11 18:05:42 +0000238 if (packageManager.canForwardTo(
239 intentToCheck, resolvedType, sourceUserId, targetUserId)) {
Tony Mak96d78f52017-05-03 18:53:21 +0100240 return forwardIntent;
241 }
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000242 } catch (RemoteException e) {
243 Slog.e(TAG, "PackageManagerService is dead?");
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000244 }
Tony Mak96d78f52017-05-03 18:53:21 +0100245 return null;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000246 }
247
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000248 /**
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000249 * Returns the userId of the managed profile for this device or UserHandle.USER_NULL if there is
250 * no managed profile.
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000251 *
252 * TODO: Remove the assumption that there is only one managed profile
253 * on the device.
254 */
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000255 private int getManagedProfile() {
Tony Mak96d78f52017-05-03 18:53:21 +0100256 List<UserInfo> relatedUsers = mInjector.getUserManager().getProfiles(UserHandle.myUserId());
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000257 for (UserInfo userInfo : relatedUsers) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000258 if (userInfo.isManagedProfile()) return userInfo.id;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000259 }
260 Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE
261 + " has been called, but there is no managed profile");
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000262 return UserHandle.USER_NULL;
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000263 }
Nicolas Prevot741abfc2015-08-11 12:03:51 +0100264
265 /**
266 * Returns the userId of the profile parent or UserHandle.USER_NULL if there is
267 * no parent.
268 */
269 private int getProfileParent() {
Tony Mak96d78f52017-05-03 18:53:21 +0100270 UserInfo parent = mInjector.getUserManager().getProfileParent(UserHandle.myUserId());
Nicolas Prevot741abfc2015-08-11 12:03:51 +0100271 if (parent == null) {
272 Slog.wtf(TAG, FORWARD_INTENT_TO_PARENT
273 + " has been called, but there is no parent");
274 return UserHandle.USER_NULL;
275 }
276 return parent.id;
277 }
Tony Mak96d78f52017-05-03 18:53:21 +0100278
279 /**
280 * Sanitize the intent in place.
281 */
arangelov590fba32020-02-11 18:05:42 +0000282 private static void sanitizeIntent(Intent intent) {
Tony Mak96d78f52017-05-03 18:53:21 +0100283 // Apps should not be allowed to target a specific package/ component in the target user.
284 intent.setPackage(null);
285 intent.setComponent(null);
286 }
287
Susi Kharraz-Post0446fab2019-02-21 09:42:31 -0500288 protected MetricsLogger getMetricsLogger() {
289 if (mMetricsLogger == null) {
290 mMetricsLogger = new MetricsLogger();
291 }
292 return mMetricsLogger;
293 }
294
Tony Mak96d78f52017-05-03 18:53:21 +0100295 @VisibleForTesting
296 protected Injector createInjector() {
297 return new InjectorImpl();
298 }
299
300 private class InjectorImpl implements Injector {
301
302 @Override
303 public IPackageManager getIPackageManager() {
304 return AppGlobals.getPackageManager();
305 }
306
307 @Override
308 public UserManager getUserManager() {
309 return getSystemService(UserManager.class);
310 }
311
312 @Override
313 public PackageManager getPackageManager() {
314 return IntentForwarderActivity.this.getPackageManager();
315 }
arangelov64439c1e2018-07-31 14:18:47 +0100316
317 @Override
318 public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
319 return getPackageManager().resolveActivityAsUser(intent, flags, userId);
320 }
321
322 @Override
323 public void showToast(int messageId, int duration) {
324 Toast.makeText(IntentForwarderActivity.this, getString(messageId), duration).show();
325 }
Tony Mak96d78f52017-05-03 18:53:21 +0100326 }
327
328 public interface Injector {
329 IPackageManager getIPackageManager();
330
331 UserManager getUserManager();
332
333 PackageManager getPackageManager();
arangelov64439c1e2018-07-31 14:18:47 +0100334
335 ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId);
336
337 void showToast(@StringRes int messageId, int duration);
Tony Mak96d78f52017-05-03 18:53:21 +0100338 }
Nicolas Prevot10fa67c2014-03-24 13:44:38 +0000339}