1 /* 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 17 package com.android.internal.app; 18 19 import android.app.Activity; 20 import android.app.ActivityManager; 21 import android.app.ActivityThread; 22 import android.app.AppGlobals; 23 import android.app.admin.DevicePolicyManager; 24 import android.content.Intent; 25 import android.content.pm.IPackageManager; 26 import android.content.pm.PackageManager; 27 import android.content.pm.UserInfo; 28 import android.os.Bundle; 29 import android.os.RemoteException; 30 import android.os.UserHandle; 31 import android.os.UserManager; 32 import android.util.Slog; 33 import android.widget.Toast; 34 35 import com.android.internal.annotations.VisibleForTesting; 36 37 import java.util.List; 38 39 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; 40 41 /** 42 * This is used in conjunction with 43 * {@link DevicePolicyManager#addCrossProfileIntentFilter} to enable intents to 44 * be passed in and out of a managed profile. 45 */ 46 public class IntentForwarderActivity extends Activity { 47 48 public static String TAG = "IntentForwarderActivity"; 49 50 public static String FORWARD_INTENT_TO_PARENT 51 = "com.android.internal.app.ForwardIntentToParent"; 52 53 public static String FORWARD_INTENT_TO_MANAGED_PROFILE 54 = "com.android.internal.app.ForwardIntentToManagedProfile"; 55 56 private Injector mInjector; 57 58 @Override onCreate(Bundle savedInstanceState)59 protected void onCreate(Bundle savedInstanceState) { 60 super.onCreate(savedInstanceState); 61 mInjector = createInjector(); 62 63 Intent intentReceived = getIntent(); 64 String className = intentReceived.getComponent().getClassName(); 65 final int targetUserId; 66 final int userMessageId; 67 if (className.equals(FORWARD_INTENT_TO_PARENT)) { 68 userMessageId = com.android.internal.R.string.forward_intent_to_owner; 69 targetUserId = getProfileParent(); 70 } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) { 71 userMessageId = com.android.internal.R.string.forward_intent_to_work; 72 targetUserId = getManagedProfile(); 73 } else { 74 Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly"); 75 userMessageId = -1; 76 targetUserId = UserHandle.USER_NULL; 77 } 78 if (targetUserId == UserHandle.USER_NULL) { 79 // This covers the case where there is no parent / managed profile. 80 finish(); 81 return; 82 } 83 84 final int callingUserId = getUserId(); 85 final Intent newIntent = canForward(intentReceived, targetUserId); 86 if (newIntent != null) { 87 if (Intent.ACTION_CHOOSER.equals(newIntent.getAction())) { 88 Intent innerIntent = newIntent.getParcelableExtra(Intent.EXTRA_INTENT); 89 // At this point, innerIntent is not null. Otherwise, canForward would have returned 90 // false. 91 innerIntent.prepareToLeaveUser(callingUserId); 92 } else { 93 newIntent.prepareToLeaveUser(callingUserId); 94 } 95 96 final android.content.pm.ResolveInfo ri = 97 mInjector.getPackageManager().resolveActivityAsUser( 98 newIntent, 99 MATCH_DEFAULT_ONLY, 100 targetUserId); 101 102 // Don't show the disclosure if next activity is ResolverActivity or ChooserActivity 103 // as those will already have shown work / personal as neccesary etc. 104 final boolean shouldShowDisclosure = ri == null || ri.activityInfo == null || 105 !"android".equals(ri.activityInfo.packageName) || 106 !(ResolverActivity.class.getName().equals(ri.activityInfo.name) 107 || ChooserActivity.class.getName().equals(ri.activityInfo.name)); 108 109 try { 110 startActivityAsCaller(newIntent, null, false, targetUserId); 111 } catch (RuntimeException e) { 112 int launchedFromUid = -1; 113 String launchedFromPackage = "?"; 114 try { 115 launchedFromUid = ActivityManager.getService().getLaunchedFromUid( 116 getActivityToken()); 117 launchedFromPackage = ActivityManager.getService().getLaunchedFromPackage( 118 getActivityToken()); 119 } catch (RemoteException ignored) { 120 } 121 122 Slog.wtf(TAG, "Unable to launch as UID " + launchedFromUid + " package " 123 + launchedFromPackage + ", while running in " 124 + ActivityThread.currentProcessName(), e); 125 } 126 127 if (shouldShowDisclosure) { 128 Toast.makeText(this, getString(userMessageId), Toast.LENGTH_LONG).show(); 129 } 130 } else { 131 Slog.wtf(TAG, "the intent: " + intentReceived + " cannot be forwarded from user " 132 + callingUserId + " to user " + targetUserId); 133 } 134 finish(); 135 } 136 137 /** 138 * Check whether the intent can be forwarded to target user. Return the intent used for 139 * forwarding if it can be forwarded, {@code null} otherwise. 140 */ canForward(Intent incomingIntent, int targetUserId)141 Intent canForward(Intent incomingIntent, int targetUserId) { 142 Intent forwardIntent = new Intent(incomingIntent); 143 forwardIntent.addFlags( 144 Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); 145 sanitizeIntent(forwardIntent); 146 147 Intent intentToCheck = forwardIntent; 148 if (Intent.ACTION_CHOOSER.equals(forwardIntent.getAction())) { 149 // The EXTRA_INITIAL_INTENTS may not be allowed to be forwarded. 150 if (forwardIntent.hasExtra(Intent.EXTRA_INITIAL_INTENTS)) { 151 Slog.wtf(TAG, "An chooser intent with extra initial intents cannot be forwarded to" 152 + " a different user"); 153 return null; 154 } 155 if (forwardIntent.hasExtra(Intent.EXTRA_REPLACEMENT_EXTRAS)) { 156 Slog.wtf(TAG, "A chooser intent with replacement extras cannot be forwarded to a" 157 + " different user"); 158 return null; 159 } 160 intentToCheck = forwardIntent.getParcelableExtra(Intent.EXTRA_INTENT); 161 if (intentToCheck == null) { 162 Slog.wtf(TAG, "Cannot forward a chooser intent with no extra " 163 + Intent.EXTRA_INTENT); 164 return null; 165 } 166 } 167 if (forwardIntent.getSelector() != null) { 168 intentToCheck = forwardIntent.getSelector(); 169 } 170 String resolvedType = intentToCheck.resolveTypeIfNeeded(getContentResolver()); 171 sanitizeIntent(intentToCheck); 172 try { 173 if (mInjector.getIPackageManager(). 174 canForwardTo(intentToCheck, resolvedType, getUserId(), targetUserId)) { 175 return forwardIntent; 176 } 177 } catch (RemoteException e) { 178 Slog.e(TAG, "PackageManagerService is dead?"); 179 } 180 return null; 181 } 182 183 /** 184 * Returns the userId of the managed profile for this device or UserHandle.USER_NULL if there is 185 * no managed profile. 186 * 187 * TODO: Remove the assumption that there is only one managed profile 188 * on the device. 189 */ getManagedProfile()190 private int getManagedProfile() { 191 List<UserInfo> relatedUsers = mInjector.getUserManager().getProfiles(UserHandle.myUserId()); 192 for (UserInfo userInfo : relatedUsers) { 193 if (userInfo.isManagedProfile()) return userInfo.id; 194 } 195 Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE 196 + " has been called, but there is no managed profile"); 197 return UserHandle.USER_NULL; 198 } 199 200 /** 201 * Returns the userId of the profile parent or UserHandle.USER_NULL if there is 202 * no parent. 203 */ getProfileParent()204 private int getProfileParent() { 205 UserInfo parent = mInjector.getUserManager().getProfileParent(UserHandle.myUserId()); 206 if (parent == null) { 207 Slog.wtf(TAG, FORWARD_INTENT_TO_PARENT 208 + " has been called, but there is no parent"); 209 return UserHandle.USER_NULL; 210 } 211 return parent.id; 212 } 213 214 /** 215 * Sanitize the intent in place. 216 */ sanitizeIntent(Intent intent)217 private void sanitizeIntent(Intent intent) { 218 // Apps should not be allowed to target a specific package/ component in the target user. 219 intent.setPackage(null); 220 intent.setComponent(null); 221 } 222 223 @VisibleForTesting createInjector()224 protected Injector createInjector() { 225 return new InjectorImpl(); 226 } 227 228 private class InjectorImpl implements Injector { 229 230 @Override getIPackageManager()231 public IPackageManager getIPackageManager() { 232 return AppGlobals.getPackageManager(); 233 } 234 235 @Override getUserManager()236 public UserManager getUserManager() { 237 return getSystemService(UserManager.class); 238 } 239 240 @Override getPackageManager()241 public PackageManager getPackageManager() { 242 return IntentForwarderActivity.this.getPackageManager(); 243 } 244 } 245 246 public interface Injector { getIPackageManager()247 IPackageManager getIPackageManager(); 248 getUserManager()249 UserManager getUserManager(); 250 getPackageManager()251 PackageManager getPackageManager(); 252 } 253 } 254