CrossProfileAppsServiceImpl.java revision de32b83499151ad8e0b47bd90cb0a87cdfb7cf01
1/* 2 * Copyright (C) 2017 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 */ 16package com.android.server.pm; 17 18import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; 19import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 20 21import android.annotation.UserIdInt; 22import android.app.ActivityManagerInternal; 23import android.app.ActivityOptions; 24import android.app.AppOpsManager; 25import android.app.IApplicationThread; 26import android.content.ComponentName; 27import android.content.Context; 28import android.content.Intent; 29import android.content.pm.ActivityInfo; 30import android.content.pm.ICrossProfileApps; 31import android.content.pm.PackageInfo; 32import android.content.pm.PackageManager; 33import android.content.pm.PackageManagerInternal; 34import android.content.pm.ResolveInfo; 35import android.os.Binder; 36import android.os.RemoteException; 37import android.os.UserHandle; 38import android.os.UserManager; 39import android.text.TextUtils; 40 41import com.android.internal.annotations.VisibleForTesting; 42import com.android.internal.util.Preconditions; 43import com.android.server.LocalServices; 44 45import java.util.ArrayList; 46import java.util.List; 47 48public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { 49 private static final String TAG = "CrossProfileAppsService"; 50 51 private Context mContext; 52 private Injector mInjector; 53 54 public CrossProfileAppsServiceImpl(Context context) { 55 this(context, new InjectorImpl(context)); 56 } 57 58 @VisibleForTesting 59 CrossProfileAppsServiceImpl(Context context, Injector injector) { 60 mContext = context; 61 mInjector = injector; 62 } 63 64 @Override 65 public List<UserHandle> getTargetUserProfiles(String callingPackage) { 66 Preconditions.checkNotNull(callingPackage); 67 68 verifyCallingPackage(callingPackage); 69 70 return getTargetUserProfilesUnchecked( 71 callingPackage, mInjector.getCallingUserId()); 72 } 73 74 @Override 75 public void startActivityAsUser( 76 IApplicationThread caller, 77 String callingPackage, 78 ComponentName component, 79 UserHandle user) throws RemoteException { 80 Preconditions.checkNotNull(callingPackage); 81 Preconditions.checkNotNull(component); 82 Preconditions.checkNotNull(user); 83 84 verifyCallingPackage(callingPackage); 85 86 List<UserHandle> allowedTargetUsers = getTargetUserProfilesUnchecked( 87 callingPackage, mInjector.getCallingUserId()); 88 if (!allowedTargetUsers.contains(user)) { 89 throw new SecurityException( 90 callingPackage + " cannot access unrelated user " + user.getIdentifier()); 91 } 92 93 // Verify that caller package is starting activity in its own package. 94 if (!callingPackage.equals(component.getPackageName())) { 95 throw new SecurityException( 96 callingPackage + " attempts to start an activity in other package - " 97 + component.getPackageName()); 98 } 99 100 final int callingUid = mInjector.getCallingUid(); 101 102 // Verify that target activity does handle the intent with ACTION_MAIN and 103 // CATEGORY_LAUNCHER as calling startActivityAsUser ignore them if component is present. 104 final Intent launchIntent = new Intent(Intent.ACTION_MAIN); 105 launchIntent.addCategory(Intent.CATEGORY_LAUNCHER); 106 launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK 107 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 108 // Only package name is set here, as opposed to component name, because intent action and 109 // category are ignored if component name is present while we are resolving intent. 110 launchIntent.setPackage(component.getPackageName()); 111 verifyActivityCanHandleIntentAndExported(launchIntent, component, callingUid, user); 112 113 launchIntent.setPackage(null); 114 launchIntent.setComponent(component); 115 mInjector.getActivityManagerInternal().startActivityAsUser( 116 caller, callingPackage, launchIntent, 117 ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(), 118 user.getIdentifier()); 119 } 120 121 private List<UserHandle> getTargetUserProfilesUnchecked( 122 String callingPackage, @UserIdInt int callingUserId) { 123 final long ident = mInjector.clearCallingIdentity(); 124 try { 125 final int[] enabledProfileIds = 126 mInjector.getUserManager().getEnabledProfileIds(callingUserId); 127 128 List<UserHandle> targetProfiles = new ArrayList<>(); 129 for (final int userId : enabledProfileIds) { 130 if (userId == callingUserId) { 131 continue; 132 } 133 if (!isPackageEnabled(callingPackage, userId)) { 134 continue; 135 } 136 targetProfiles.add(UserHandle.of(userId)); 137 } 138 return targetProfiles; 139 } finally { 140 mInjector.restoreCallingIdentity(ident); 141 } 142 } 143 144 private boolean isPackageEnabled(String packageName, @UserIdInt int userId) { 145 final int callingUid = mInjector.getCallingUid(); 146 final long ident = mInjector.clearCallingIdentity(); 147 try { 148 final PackageInfo info = mInjector.getPackageManagerInternal() 149 .getPackageInfo( 150 packageName, 151 MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, 152 callingUid, 153 userId); 154 return info != null && info.applicationInfo.enabled; 155 } finally { 156 mInjector.restoreCallingIdentity(ident); 157 } 158 } 159 160 /** 161 * Verify that the specified intent does resolved to the specified component and the resolved 162 * activity is exported. 163 */ 164 private void verifyActivityCanHandleIntentAndExported( 165 Intent launchIntent, ComponentName component, int callingUid, UserHandle user) { 166 final long ident = mInjector.clearCallingIdentity(); 167 try { 168 final List<ResolveInfo> apps = 169 mInjector.getPackageManagerInternal().queryIntentActivities( 170 launchIntent, 171 MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, 172 callingUid, 173 user.getIdentifier()); 174 final int size = apps.size(); 175 for (int i = 0; i < size; ++i) { 176 final ActivityInfo activityInfo = apps.get(i).activityInfo; 177 if (TextUtils.equals(activityInfo.packageName, component.getPackageName()) 178 && TextUtils.equals(activityInfo.name, component.getClassName()) 179 && activityInfo.exported) { 180 return; 181 } 182 } 183 throw new SecurityException("Attempt to launch activity without " 184 + " category Intent.CATEGORY_LAUNCHER or activity is not exported" + component); 185 } finally { 186 mInjector.restoreCallingIdentity(ident); 187 } 188 } 189 190 /** 191 * Verify that the given calling package is belong to the calling UID. 192 */ 193 private void verifyCallingPackage(String callingPackage) { 194 mInjector.getAppOpsManager().checkPackage(mInjector.getCallingUid(), callingPackage); 195 } 196 197 private static class InjectorImpl implements Injector { 198 private Context mContext; 199 200 public InjectorImpl(Context context) { 201 mContext = context; 202 } 203 204 public int getCallingUid() { 205 return Binder.getCallingUid(); 206 } 207 208 public int getCallingUserId() { 209 return UserHandle.getCallingUserId(); 210 } 211 212 public UserHandle getCallingUserHandle() { 213 return Binder.getCallingUserHandle(); 214 } 215 216 public long clearCallingIdentity() { 217 return Binder.clearCallingIdentity(); 218 } 219 220 public void restoreCallingIdentity(long token) { 221 Binder.restoreCallingIdentity(token); 222 } 223 224 public UserManager getUserManager() { 225 return mContext.getSystemService(UserManager.class); 226 } 227 228 public PackageManagerInternal getPackageManagerInternal() { 229 return LocalServices.getService(PackageManagerInternal.class); 230 } 231 232 public PackageManager getPackageManager() { 233 return mContext.getPackageManager(); 234 } 235 236 public AppOpsManager getAppOpsManager() { 237 return mContext.getSystemService(AppOpsManager.class); 238 } 239 240 @Override 241 public ActivityManagerInternal getActivityManagerInternal() { 242 return LocalServices.getService(ActivityManagerInternal.class); 243 } 244 } 245 246 @VisibleForTesting 247 public interface Injector { 248 int getCallingUid(); 249 250 int getCallingUserId(); 251 252 UserHandle getCallingUserHandle(); 253 254 long clearCallingIdentity(); 255 256 void restoreCallingIdentity(long token); 257 258 UserManager getUserManager(); 259 260 PackageManagerInternal getPackageManagerInternal(); 261 262 PackageManager getPackageManager(); 263 264 AppOpsManager getAppOpsManager(); 265 266 ActivityManagerInternal getActivityManagerInternal(); 267 } 268} 269