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