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