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  */
16 package android.content.pm;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 import android.annotation.RequiresPermission;
21 import android.annotation.SystemApi;
22 import android.annotation.TestApi;
23 import android.app.Activity;
24 import android.app.AppOpsManager.Mode;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.res.Resources;
29 import android.graphics.drawable.Drawable;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.os.RemoteException;
33 import android.os.UserHandle;
34 import android.os.UserManager;
35 import android.provider.Settings;
36 
37 import com.android.internal.R;
38 import com.android.internal.util.UserIcons;
39 
40 import java.util.Collection;
41 import java.util.List;
42 import java.util.Set;
43 import java.util.stream.Collectors;
44 
45 /**
46  * Class for handling cross profile operations. Apps can use this class to interact with its
47  * instance in any profile that is in {@link #getTargetUserProfiles()}. For example, app can
48  * use this class to start its main activity in managed profile.
49  */
50 public class CrossProfileApps {
51 
52     /**
53      * Broadcast signalling that the receiving app's permission to interact across profiles has
54      * changed. This includes the user, admin, or OEM changing their consent such that the
55      * permission for the app to interact across profiles has changed.
56      *
57      * <p>This broadcast is not sent when other circumstances result in a change to being able to
58      * interact across profiles in practice, such as the profile being turned off or removed, apps
59      * being uninstalled, etc. The methods {@link #canInteractAcrossProfiles()} and {@link
60      * #canRequestInteractAcrossProfiles()} can be used by apps prior to attempting to interact
61      * across profiles or attempting to request user consent to interact across profiles.
62      *
63      * <p>Apps that have set the {@code android:crossProfile} manifest attribute to {@code true}
64      * can receive this broadcast in manifest broadcast receivers. Otherwise, it can only be
65      * received by dynamically-registered broadcast receivers.
66      */
67     public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED =
68             "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED";
69 
70     private final Context mContext;
71     private final ICrossProfileApps mService;
72     private final UserManager mUserManager;
73     private final Resources mResources;
74 
75     /** @hide */
CrossProfileApps(Context context, ICrossProfileApps service)76     public CrossProfileApps(Context context, ICrossProfileApps service) {
77         mContext = context;
78         mService = service;
79         mUserManager = context.getSystemService(UserManager.class);
80         mResources = context.getResources();
81     }
82 
83     /**
84      * Starts the specified main activity of the caller package in the specified profile.
85      *
86      * @param component The ComponentName of the activity to launch, it must be exported and has
87      *        action {@link android.content.Intent#ACTION_MAIN}, category
88      *        {@link android.content.Intent#CATEGORY_LAUNCHER}. Otherwise, SecurityException will
89      *        be thrown.
90      * @param targetUser The UserHandle of the profile, must be one of the users returned by
91      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
92      *        be thrown.
93      */
startMainActivity(@onNull ComponentName component, @NonNull UserHandle targetUser)94     public void startMainActivity(@NonNull ComponentName component,
95             @NonNull UserHandle targetUser) {
96         try {
97             mService.startActivityAsUser(
98                     mContext.getIApplicationThread(),
99                     mContext.getPackageName(),
100                     mContext.getAttributionTag(),
101                     component,
102                     targetUser.getIdentifier(),
103                     true);
104         } catch (RemoteException ex) {
105             throw ex.rethrowFromSystemServer();
106         }
107     }
108 
109     /**
110      * Starts the specified activity of the caller package in the specified profile.
111      *
112      * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES},
113      * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}, or {@code
114      * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. Both the caller and
115      * target user profiles must be in the same profile group. The target user must be a valid user
116      * returned from {@link #getTargetUserProfiles()}.
117      *
118      * @param intent The intent to launch. A component in the caller package must be specified.
119      * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by
120      *        {@link #getTargetUserProfiles()} if different to the calling user, otherwise a
121      *        {@link SecurityException} will be thrown.
122      * @param callingActivity The activity to start the new activity from for the purposes of
123      *        passing back any result and deciding which task the new activity should belong to. If
124      *        {@code null}, the activity will always be started in a new task and no result will be
125      *        returned.
126      */
127     @RequiresPermission(anyOf = {
128             android.Manifest.permission.INTERACT_ACROSS_PROFILES,
129             android.Manifest.permission.INTERACT_ACROSS_USERS})
startActivity( @onNull Intent intent, @NonNull UserHandle targetUser, @Nullable Activity callingActivity)130     public void startActivity(
131             @NonNull Intent intent,
132             @NonNull UserHandle targetUser,
133             @Nullable Activity callingActivity) {
134         startActivity(intent, targetUser, callingActivity, /* options= */ null);
135     }
136 
137     /**
138      * Starts the specified activity of the caller package in the specified profile.
139      *
140      * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES},
141      * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}, or {@code
142      * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. Both the caller and
143      * target user profiles must be in the same profile group. The target user must be a valid user
144      * returned from {@link #getTargetUserProfiles()}.
145      *
146      * @param intent The intent to launch. A component in the caller package must be specified.
147      * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by
148      *        {@link #getTargetUserProfiles()} if different to the calling user, otherwise a
149      *        {@link SecurityException} will be thrown.
150      * @param callingActivity The activity to start the new activity from for the purposes of
151      *        passing back any result and deciding which task the new activity should belong to. If
152      *        {@code null}, the activity will always be started in a new task and no result will be
153      *        returned.
154      * @param options The activity options or {@code null}. See {@link android.app.ActivityOptions}.
155      */
156     @RequiresPermission(anyOf = {
157             android.Manifest.permission.INTERACT_ACROSS_PROFILES,
158             android.Manifest.permission.INTERACT_ACROSS_USERS})
startActivity( @onNull Intent intent, @NonNull UserHandle targetUser, @Nullable Activity callingActivity, @Nullable Bundle options)159     public void startActivity(
160             @NonNull Intent intent,
161             @NonNull UserHandle targetUser,
162             @Nullable Activity callingActivity,
163             @Nullable Bundle options) {
164         try {
165             mService.startActivityAsUserByIntent(
166                     mContext.getIApplicationThread(),
167                     mContext.getPackageName(),
168                     mContext.getAttributionTag(),
169                     intent,
170                     targetUser.getIdentifier(),
171                     callingActivity != null ? callingActivity.getActivityToken() : null,
172                     options);
173         } catch (RemoteException ex) {
174             throw ex.rethrowFromSystemServer();
175         }
176     }
177 
178     /**
179      * Starts the specified activity of the caller package in the specified profile. Unlike
180      * {@link #startMainActivity}, this can start any activity of the caller package, not just
181      * the main activity.
182      * The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
183      * permission and both the caller and target user profiles must be in the same profile group.
184      *
185      * @param component The ComponentName of the activity to launch. It must be exported.
186      * @param targetUser The UserHandle of the profile, must be one of the users returned by
187      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
188      *        be thrown.
189      * @hide
190      */
191     @SystemApi
192     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)
startActivity(@onNull ComponentName component, @NonNull UserHandle targetUser)193     public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
194         try {
195             mService.startActivityAsUser(mContext.getIApplicationThread(),
196                     mContext.getPackageName(), mContext.getAttributionTag(), component,
197                     targetUser.getIdentifier(), false);
198         } catch (RemoteException ex) {
199             throw ex.rethrowFromSystemServer();
200         }
201     }
202 
203     /**
204      * Return a list of user profiles that that the caller can use when calling other APIs in this
205      * class.
206      * <p>
207      * A user profile would be considered as a valid target user profile, provided that:
208      * <ul>
209      * <li>It gets caller app installed</li>
210      * <li>It is not equal to the calling user</li>
211      * <li>It is in the same profile group of calling user profile</li>
212      * <li>It is enabled</li>
213      * </ul>
214      *
215      * @see UserManager#getUserProfiles()
216      */
getTargetUserProfiles()217     public @NonNull List<UserHandle> getTargetUserProfiles() {
218         try {
219             return mService.getTargetUserProfiles(mContext.getPackageName());
220         } catch (RemoteException ex) {
221             throw ex.rethrowFromSystemServer();
222         }
223     }
224 
225     /**
226      * Return a label that calling app can show to user for the semantic of profile switching --
227      * launching its own activity in specified user profile. For example, it may return
228      * "Switch to work" if the given user handle is the managed profile one.
229      *
230      * @param userHandle The UserHandle of the target profile, must be one of the users returned by
231      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
232      *        be thrown.
233      * @return a label that calling app can show user for the semantic of launching its own
234      *         activity in the specified user profile.
235      *
236      * @see #startMainActivity(ComponentName, UserHandle)
237      */
getProfileSwitchingLabel(@onNull UserHandle userHandle)238     public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) {
239         verifyCanAccessUser(userHandle);
240 
241         final int stringRes = mUserManager.isManagedProfile(userHandle.getIdentifier())
242                 ? R.string.managed_profile_label
243                 : R.string.user_owner_label;
244         return mResources.getString(stringRes);
245     }
246 
247     /**
248      * Return a drawable that calling app can show to user for the semantic of profile switching --
249      * launching its own activity in specified user profile. For example, it may return a briefcase
250      * icon if the given user handle is the managed profile one.
251      *
252      * @param userHandle The UserHandle of the target profile, must be one of the users returned by
253      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
254      *        be thrown.
255      * @return an icon that calling app can show user for the semantic of launching its own
256      *         activity in specified user profile.
257      *
258      * @see #startMainActivity(ComponentName, UserHandle)
259      */
getProfileSwitchingIconDrawable(@onNull UserHandle userHandle)260     public @NonNull Drawable getProfileSwitchingIconDrawable(@NonNull UserHandle userHandle) {
261         verifyCanAccessUser(userHandle);
262 
263         final boolean isManagedProfile =
264                 mUserManager.isManagedProfile(userHandle.getIdentifier());
265         if (isManagedProfile) {
266             return mResources.getDrawable(R.drawable.ic_corp_badge, null);
267         } else {
268             return UserIcons.getDefaultUserIcon(
269                     mResources, UserHandle.USER_SYSTEM, true /* light */);
270         }
271     }
272 
273     /**
274      * Returns whether the calling package can request to navigate the user to
275      * the relevant settings page to request user consent to interact across profiles.
276      *
277      * <p>If {@code true}, the navigation intent can be obtained via {@link
278      * #createRequestInteractAcrossProfilesIntent()}. The package can then listen to {@link
279      * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts.
280      *
281      * <p>Specifically, returns whether the following are all true:
282      * <ul>
283      * <li>{@code UserManager#getEnabledProfileIds(int)} returns at least one other profile for the
284      * calling user.</li>
285      * <li>The calling app has requested
286      * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest.</li>
287      * <li>The calling app is not a profile owner within the profile group of the calling user.</li>
288      * </ul>
289      *
290      * <p>Note that in order for the user to be able to grant the consent, the requesting package
291      * must be allowlisted by the admin or the OEM and installed in the other profile. If this is
292      * not the case the user will be shown a message explaining why they can't grant the consent.
293      *
294      * <p>Note that user consent could already be granted if given a return value of {@code true}.
295      * The package's current ability to interact across profiles can be checked with {@link
296      * #canInteractAcrossProfiles()}.
297      *
298      * @return true if the calling package can request to interact across profiles.
299      */
canRequestInteractAcrossProfiles()300     public boolean canRequestInteractAcrossProfiles() {
301         try {
302             return mService.canRequestInteractAcrossProfiles(mContext.getPackageName());
303         } catch (RemoteException ex) {
304             throw ex.rethrowFromSystemServer();
305         }
306     }
307 
308     /**
309      * Returns whether the calling package can interact across profiles.
310 
311      * <p>Specifically, returns whether the following are all true:
312      * <ul>
313      * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li>
314      * <li>The user has previously consented to cross-profile communication for the calling
315      * package.</li>
316      * <li>The calling package has either been allowlisted by default by the OEM or has been
317      * explicitly allowlisted by the admin via
318      * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}.
319      * </li>
320      * </ul>
321      *
322      * <p>If {@code false}, the package's current ability to request user consent to interact across
323      * profiles can be checked with {@link #canRequestInteractAcrossProfiles()}. If {@code true},
324      * user consent can be obtained via {@link #createRequestInteractAcrossProfilesIntent()}. The
325      * package can then listen to {@link #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts.
326      *
327      * @return true if the calling package can interact across profiles.
328      * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the
329      * calling UID.
330      */
canInteractAcrossProfiles()331     public boolean canInteractAcrossProfiles() {
332         try {
333             return mService.canInteractAcrossProfiles(mContext.getPackageName());
334         } catch (RemoteException ex) {
335             throw ex.rethrowFromSystemServer();
336         }
337     }
338 
339     /**
340      * Returns an {@link Intent} to open the settings page that allows the user to decide whether
341      * the calling app can interact across profiles.
342      *
343      * <p>The method {@link #canRequestInteractAcrossProfiles()} must be returning {@code true}.
344      *
345      * <p>Note that the user may already have given consent and the app may already be able to
346      * interact across profiles, even if {@link #canRequestInteractAcrossProfiles()} is {@code
347      * true}. The current ability to interact across profiles is given by {@link
348      * #canInteractAcrossProfiles()}.
349      *
350      * @return an {@link Intent} to open the settings page that allows the user to decide whether
351      * the app can interact across profiles
352      *
353      * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the
354      * calling UID, or {@link #canRequestInteractAcrossProfiles()} is {@code false}.
355      */
createRequestInteractAcrossProfilesIntent()356     public @NonNull Intent createRequestInteractAcrossProfilesIntent() {
357         if (!canRequestInteractAcrossProfiles()) {
358             throw new SecurityException(
359                     "The calling package can not request to interact across profiles.");
360         }
361         final Intent settingsIntent = new Intent();
362         settingsIntent.setAction(Settings.ACTION_MANAGE_CROSS_PROFILE_ACCESS);
363         final Uri packageUri = Uri.parse("package:" + mContext.getPackageName());
364         settingsIntent.setData(packageUri);
365         return settingsIntent;
366     }
367 
368     /**
369      * Sets the app-op for {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} that is
370      * configurable by users in Settings. This configures it for the profile group of the calling
371      * package.
372      *
373      * <p>Before calling, check {@link #canConfigureInteractAcrossProfiles(String)} and do not call
374      * if it is {@code false}. If presenting a user interface, do not allow the user to configure
375      * the app-op in that case.
376      *
377      * <p>The underlying app-op {@link android.app.AppOpsManager#OP_INTERACT_ACROSS_PROFILES} should
378      * never be set directly. This method ensures that the app-op is kept in sync for the app across
379      * each user in the profile group and that those apps are sent a broadcast when their ability to
380      * interact across profiles changes.
381      *
382      * <p>This method should be used directly whenever a user's action results in a change in an
383      * app's ability to interact across profiles, as defined by the return value of {@link
384      * #canInteractAcrossProfiles()}. This includes user consent changes in Settings or during
385      * provisioning.
386      *
387      * <p>If other changes could have affected the app's ability to interact across profiles, as
388      * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the
389      * admin or OEM consent whitelists, then {@link #resetInteractAcrossProfilesAppOps(Collection,
390      * Set)} should be used.
391      *
392      * <p>If the caller does not have the {@link android.Manifest.permission
393      * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that
394      * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String,
395      * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}.
396      *
397      * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link
398      * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
399      *
400      * @hide
401      */
402     @RequiresPermission(
403             allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
404                     android.Manifest.permission.INTERACT_ACROSS_USERS})
setInteractAcrossProfilesAppOp(@onNull String packageName, @Mode int newMode)405     public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) {
406         try {
407             mService.setInteractAcrossProfilesAppOp(packageName, newMode);
408         } catch (RemoteException ex) {
409             throw ex.rethrowFromSystemServer();
410         }
411     }
412 
413     /**
414      * Returns whether the given package can have its ability to interact across profiles configured
415      * by the user. This means that every other condition to interact across profiles has been set.
416      *
417      * <p>This differs from {@link #canRequestInteractAcrossProfiles()} since it will not return
418      * {@code false} simply when the target profile is disabled.
419      *
420      * @hide
421      */
422     @TestApi
canConfigureInteractAcrossProfiles(@onNull String packageName)423     public boolean canConfigureInteractAcrossProfiles(@NonNull String packageName) {
424         try {
425             return mService.canConfigureInteractAcrossProfiles(packageName);
426         } catch (RemoteException ex) {
427             throw ex.rethrowFromSystemServer();
428         }
429     }
430 
431     /**
432      * Returns {@code true} if the given package has requested
433      * {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} and the user has at least one
434      * other profile in the same profile group.
435      *
436      * <p>This differs from {@link #canConfigureInteractAcrossProfiles(String)} since it will
437      * not return {@code false} if the app is not allowlisted or not installed in the other profile.
438      *
439      * <p>Note that platform-signed apps that are automatically granted the permission and are not
440      * allowlisted by the OEM will not be included in this list.
441      *
442      * @hide
443      */
canUserAttemptToConfigureInteractAcrossProfiles(String packageName)444     public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) {
445         try {
446             return mService.canUserAttemptToConfigureInteractAcrossProfiles(packageName);
447         } catch (RemoteException ex) {
448             throw ex.rethrowFromSystemServer();
449         }
450     }
451     /**
452      * For each of the packages defined in {@code previousCrossProfilePackages} but not included in
453      * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission
454      * #INTERACT_ACROSS_PROFILES} back to its default value if it can no longer be configured by
455      * users in Settings, as defined by {@link #canConfigureInteractAcrossProfiles(String)}.
456      *
457      * <p>This method should be used whenever an app's ability to interact across profiles could
458      * have changed as a result of non-user actions, such as changes to admin or OEM consent
459      * whitelists.
460      *
461      * <p>If the caller does not have the {@link android.Manifest.permission
462      * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that
463      * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String,
464      * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}.
465      *
466      * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link
467      * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
468      *
469      * @hide
470      */
471     @RequiresPermission(
472             allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
473                     android.Manifest.permission.INTERACT_ACROSS_USERS})
resetInteractAcrossProfilesAppOps( @onNull Collection<String> previousCrossProfilePackages, @NonNull Set<String> newCrossProfilePackages)474     public void resetInteractAcrossProfilesAppOps(
475             @NonNull Collection<String> previousCrossProfilePackages,
476             @NonNull Set<String> newCrossProfilePackages) {
477         if (previousCrossProfilePackages.isEmpty()) {
478             return;
479         }
480         final List<String> unsetCrossProfilePackages =
481                 previousCrossProfilePackages.stream()
482                         .filter(packageName -> !newCrossProfilePackages.contains(packageName))
483                         .collect(Collectors.toList());
484         if (unsetCrossProfilePackages.isEmpty()) {
485             return;
486         }
487         try {
488             mService.resetInteractAcrossProfilesAppOps(unsetCrossProfilePackages);
489         } catch (RemoteException ex) {
490             throw ex.rethrowFromSystemServer();
491         }
492     }
493 
494     /**
495      * Clears the app-op for {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} back to
496      * its default value for every package on the device.
497      *
498      * <p>This method can be used to ensure that app-op state is not left around on existing users
499      * for previously-configured profiles.
500      *
501      * <p>If the caller does not have the {@link android.Manifest.permission
502      * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that
503      * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String,
504      * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}.
505      *
506      * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link
507      * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
508      *
509      * @hide
510      */
511     @RequiresPermission(
512             allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
513                     android.Manifest.permission.INTERACT_ACROSS_USERS})
clearInteractAcrossProfilesAppOps()514     public void clearInteractAcrossProfilesAppOps() {
515         try {
516             mService.clearInteractAcrossProfilesAppOps();
517         } catch (RemoteException ex) {
518             throw ex.rethrowFromSystemServer();
519         }
520     }
521 
verifyCanAccessUser(UserHandle userHandle)522     private void verifyCanAccessUser(UserHandle userHandle) {
523         if (!getTargetUserProfiles().contains(userHandle)) {
524             throw new SecurityException("Not allowed to access " + userHandle);
525         }
526     }
527 }
528