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 static android.Manifest.permission.INTERACT_ACROSS_USERS;
19 import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
20 import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
21 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
22 import static android.app.admin.flags.Flags.FLAG_ALLOW_QUERYING_PROFILE_TYPE;
23 
24 import android.annotation.FlaggedApi;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.RequiresPermission;
28 import android.annotation.SystemApi;
29 import android.annotation.TestApi;
30 import android.annotation.UserHandleAware;
31 import android.app.Activity;
32 import android.app.ActivityOptions;
33 import android.app.AppOpsManager.Mode;
34 import android.app.admin.DevicePolicyManager;
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.res.Resources;
39 import android.graphics.drawable.Drawable;
40 import android.net.Uri;
41 import android.os.Build;
42 import android.os.Bundle;
43 import android.os.RemoteException;
44 import android.os.UserHandle;
45 import android.os.UserManager;
46 import android.provider.Settings;
47 import android.text.TextUtils;
48 
49 import com.android.internal.R;
50 import com.android.internal.util.UserIcons;
51 
52 import java.util.Collection;
53 import java.util.List;
54 import java.util.Set;
55 import java.util.stream.Collectors;
56 
57 /**
58  * Class for handling cross profile operations. Apps can use this class to interact with its
59  * instance in any profile that is in {@link #getTargetUserProfiles()}. For example, app can
60  * use this class to start its main activity in managed profile.
61  */
62 public class CrossProfileApps {
63 
64     /**
65      * Broadcast signalling that the receiving app's permission to interact across profiles has
66      * changed. This includes the user, admin, or OEM changing their consent such that the
67      * permission for the app to interact across profiles has changed.
68      *
69      * <p>This broadcast is not sent when other circumstances result in a change to being able to
70      * interact across profiles in practice, such as the profile being turned off or removed, apps
71      * being uninstalled, etc. The methods {@link #canInteractAcrossProfiles()} and {@link
72      * #canRequestInteractAcrossProfiles()} can be used by apps prior to attempting to interact
73      * across profiles or attempting to request user consent to interact across profiles.
74      *
75      * <p>Apps that have set the {@code android:crossProfile} manifest attribute to {@code true}
76      * can receive this broadcast in manifest broadcast receivers. Otherwise, it can only be
77      * received by dynamically-registered broadcast receivers.
78      */
79     public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED =
80             "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED";
81 
82     private final Context mContext;
83     private final ICrossProfileApps mService;
84     private final UserManager mUserManager;
85     private final Resources mResources;
86 
87     /** @hide */
CrossProfileApps(Context context, ICrossProfileApps service)88     public CrossProfileApps(Context context, ICrossProfileApps service) {
89         mContext = context;
90         mService = service;
91         mUserManager = context.getSystemService(UserManager.class);
92         mResources = context.getResources();
93     }
94 
95     /**
96      * Starts the specified main activity of the caller package in the specified profile.
97      *
98      * @param component The ComponentName of the activity to launch, it must be exported and has
99      *        action {@link android.content.Intent#ACTION_MAIN}, category
100      *        {@link android.content.Intent#CATEGORY_LAUNCHER}. Otherwise, SecurityException will
101      *        be thrown.
102      * @param targetUser The UserHandle of the profile, must be one of the users returned by
103      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
104      *        be thrown.
105      */
startMainActivity(@onNull ComponentName component, @NonNull UserHandle targetUser)106     public void startMainActivity(@NonNull ComponentName component,
107             @NonNull UserHandle targetUser) {
108         try {
109             mService.startActivityAsUser(
110                     mContext.getIApplicationThread(),
111                     mContext.getPackageName(),
112                     mContext.getAttributionTag(),
113                     component,
114                     targetUser.getIdentifier(),
115                     true,
116                     mContext.getActivityToken(),
117                     ActivityOptions.makeBasic().toBundle());
118         } catch (RemoteException ex) {
119             throw ex.rethrowFromSystemServer();
120         }
121     }
122 
123     /**
124      * Starts the specified main activity of the caller package in the specified profile, launching
125      * in the specified activity.
126      *
127      * @param component The ComponentName of the activity to launch, it must be exported and has
128      *        action {@link android.content.Intent#ACTION_MAIN}, category
129      *        {@link android.content.Intent#CATEGORY_LAUNCHER}. Otherwise, SecurityException will
130      *        be thrown.
131      * @param targetUser The UserHandle of the profile, must be one of the users returned by
132      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
133      *        be thrown.
134      * @param callingActivity The activity to start the new activity from for the purposes of
135      *        deciding which task the new activity should belong to. If {@code null}, the activity
136      *        will always be started in a new task.
137      * @param options The activity options or {@code null}. See {@link android.app.ActivityOptions}.
138      */
startMainActivity(@onNull ComponentName component, @NonNull UserHandle targetUser, @Nullable Activity callingActivity, @Nullable Bundle options)139     public void startMainActivity(@NonNull ComponentName component,
140             @NonNull UserHandle targetUser,
141             @Nullable Activity callingActivity,
142             @Nullable Bundle options) {
143         try {
144             mService.startActivityAsUser(
145                     mContext.getIApplicationThread(),
146                     mContext.getPackageName(),
147                     mContext.getAttributionTag(),
148                     component,
149                     targetUser.getIdentifier(),
150                     true,
151                     callingActivity != null ? callingActivity.getActivityToken() : null,
152                     options);
153         } catch (RemoteException ex) {
154             throw ex.rethrowFromSystemServer();
155         }
156     }
157 
158     /**
159      * Starts the specified activity of the caller package in the specified profile.
160      *
161      * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES},
162      * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}, or {@code
163      * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. Both the caller and
164      * target user profiles must be in the same profile group. The target user must be a valid user
165      * returned from {@link #getTargetUserProfiles()}.
166      *
167      * @param intent The intent to launch. A component in the caller package must be specified.
168      * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by
169      *        {@link #getTargetUserProfiles()} if different to the calling user, otherwise a
170      *        {@link SecurityException} will be thrown.
171      * @param callingActivity The activity to start the new activity from for the purposes of
172      *        passing back any result and deciding which task the new activity should belong to. If
173      *        {@code null}, the activity will always be started in a new task and no result will be
174      *        returned.
175      */
176     @RequiresPermission(anyOf = {
177             android.Manifest.permission.INTERACT_ACROSS_PROFILES,
178             INTERACT_ACROSS_USERS})
startActivity( @onNull Intent intent, @NonNull UserHandle targetUser, @Nullable Activity callingActivity)179     public void startActivity(
180             @NonNull Intent intent,
181             @NonNull UserHandle targetUser,
182             @Nullable Activity callingActivity) {
183         startActivity(intent, targetUser, callingActivity, /* options= */ null);
184     }
185 
186     /**
187      * Starts the specified activity of the caller package in the specified profile.
188      *
189      * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES},
190      * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}, or {@code
191      * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. Both the caller and
192      * target user profiles must be in the same profile group. The target user must be a valid user
193      * returned from {@link #getTargetUserProfiles()}.
194      *
195      * @param intent The intent to launch. A component in the caller package must be specified.
196      * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by
197      *        {@link #getTargetUserProfiles()} if different to the calling user, otherwise a
198      *        {@link SecurityException} will be thrown.
199      * @param callingActivity The activity to start the new activity from for the purposes of
200      *        passing back any result and deciding which task the new activity should belong to. If
201      *        {@code null}, the activity will always be started in a new task and no result will be
202      *        returned.
203      * @param options The activity options or {@code null}. See {@link android.app.ActivityOptions}.
204      */
205     @RequiresPermission(anyOf = {
206             android.Manifest.permission.INTERACT_ACROSS_PROFILES,
207             INTERACT_ACROSS_USERS})
startActivity( @onNull Intent intent, @NonNull UserHandle targetUser, @Nullable Activity callingActivity, @Nullable Bundle options)208     public void startActivity(
209             @NonNull Intent intent,
210             @NonNull UserHandle targetUser,
211             @Nullable Activity callingActivity,
212             @Nullable Bundle options) {
213         try {
214             mService.startActivityAsUserByIntent(
215                     mContext.getIApplicationThread(),
216                     mContext.getPackageName(),
217                     mContext.getAttributionTag(),
218                     intent,
219                     targetUser.getIdentifier(),
220                     callingActivity != null ? callingActivity.getActivityToken() : null,
221                     options);
222         } catch (RemoteException ex) {
223             throw ex.rethrowFromSystemServer();
224         }
225     }
226 
227     /**
228      * Starts the specified activity of the caller package in the specified profile. Unlike
229      * {@link #startMainActivity}, this can start any activity of the caller package, not just
230      * the main activity.
231      * The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
232      * or {@link android.Manifest.permission#START_CROSS_PROFILE_ACTIVITIES}
233      * permission and both the caller and target user profiles must be in the same profile group.
234      *
235      * @param component The ComponentName of the activity to launch. It must be exported.
236      * @param targetUser The UserHandle of the profile, must be one of the users returned by
237      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
238      *        be thrown.
239      * @param callingActivity The activity to start the new activity from for the purposes of
240      *        deciding which task the new activity should belong to. If {@code null}, the activity
241      *        will always be started in a new task.
242      * @param options The activity options or {@code null}. See {@link android.app.ActivityOptions}.
243      * @hide
244      */
245     @SystemApi
246     @RequiresPermission(anyOf = {
247             android.Manifest.permission.INTERACT_ACROSS_PROFILES,
248             android.Manifest.permission.START_CROSS_PROFILE_ACTIVITIES})
startActivity( @onNull ComponentName component, @NonNull UserHandle targetUser, @Nullable Activity callingActivity, @Nullable Bundle options)249     public void startActivity(
250             @NonNull ComponentName component,
251             @NonNull UserHandle targetUser,
252             @Nullable Activity callingActivity,
253             @Nullable Bundle options) {
254         try {
255             mService.startActivityAsUser(
256                     mContext.getIApplicationThread(),
257                     mContext.getPackageName(),
258                     mContext.getAttributionTag(),
259                     component,
260                     targetUser.getIdentifier(),
261                     false,
262                     callingActivity != null ? callingActivity.getActivityToken() : null,
263                     options);
264         } catch (RemoteException ex) {
265             throw ex.rethrowFromSystemServer();
266         }
267     }
268 
269     /**
270      * Starts the specified activity of the caller package in the specified profile. Unlike
271      * {@link #startMainActivity}, this can start any activity of the caller package, not just
272      * the main activity.
273      * The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
274      * or {@link android.Manifest.permission#START_CROSS_PROFILE_ACTIVITIES}
275      * permission and both the caller and target user profiles must be in the same profile group.
276      *
277      * @param component The ComponentName of the activity to launch. It must be exported.
278      * @param targetUser The UserHandle of the profile, must be one of the users returned by
279      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
280      *        be thrown.
281      * @hide
282      */
283     @SystemApi
284     @RequiresPermission(anyOf = {
285             android.Manifest.permission.INTERACT_ACROSS_PROFILES,
286             android.Manifest.permission.START_CROSS_PROFILE_ACTIVITIES})
startActivity(@onNull ComponentName component, @NonNull UserHandle targetUser)287     public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
288         try {
289             mService.startActivityAsUser(mContext.getIApplicationThread(),
290                     mContext.getPackageName(), mContext.getAttributionTag(), component,
291                     targetUser.getIdentifier(), false, null, null);
292         } catch (RemoteException ex) {
293             throw ex.rethrowFromSystemServer();
294         }
295     }
296 
297     /**
298      * Return a list of user profiles that that the caller can use when calling other APIs in this
299      * class.
300      * <p>
301      * A user profile would be considered as a valid target user profile, provided that:
302      * <ul>
303      * <li>It gets caller app installed</li>
304      * <li>It is not equal to the calling user</li>
305      * <li>It is in the same profile group of calling user profile</li>
306      * <li>It is enabled</li>
307      * <li>It is not hidden (ex. profile type {@link UserManager#USER_TYPE_PROFILE_PRIVATE})</li>
308      * </ul>
309      *
310      * @see UserManager#getUserProfiles()
311      */
getTargetUserProfiles()312     public @NonNull List<UserHandle> getTargetUserProfiles() {
313         try {
314             return mService.getTargetUserProfiles(mContext.getPackageName());
315         } catch (RemoteException ex) {
316             throw ex.rethrowFromSystemServer();
317         }
318     }
319 
320 
321     /**
322      * Checks if the specified user is a profile, i.e. not the parent user.
323      *
324      * @param userHandle The UserHandle of the target profile, must be one of the users returned by
325      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
326      *        be thrown.
327      * @return whether the specified user is a profile.
328      */
329     @FlaggedApi(FLAG_ALLOW_QUERYING_PROFILE_TYPE)
330     @SuppressWarnings("UserHandleName")
isProfile(@onNull UserHandle userHandle)331     public boolean isProfile(@NonNull UserHandle userHandle) {
332         // Note that this is not a security check, but rather a check for correct use.
333         // The actual security check is performed by UserManager.
334         verifyCanAccessUser(userHandle);
335 
336         return mUserManager.isProfile(userHandle.getIdentifier());
337     }
338 
339     /**
340      * Checks if the specified user is a managed profile.
341      *
342      * @param userHandle The UserHandle of the target profile, must be one of the users returned by
343      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
344      *        be thrown.
345      * @return whether the specified user is a managed profile.
346      */
347     @FlaggedApi(FLAG_ALLOW_QUERYING_PROFILE_TYPE)
348     @SuppressWarnings("UserHandleName")
isManagedProfile(@onNull UserHandle userHandle)349     public boolean isManagedProfile(@NonNull UserHandle userHandle) {
350         // Note that this is not a security check, but rather a check for correct use.
351         // The actual security check is performed by UserManager.
352         verifyCanAccessUser(userHandle);
353 
354         return mUserManager.isManagedProfile(userHandle.getIdentifier());
355     }
356 
357     /**
358      * Return a label that calling app can show to user for the semantic of profile switching --
359      * launching its own activity in specified user profile. For example, it may return
360      * "Switch to work" if the given user handle is the managed profile one.
361      *
362      * @param userHandle The UserHandle of the target profile, must be one of the users returned by
363      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
364      *        be thrown.
365      * @return a label that calling app can show user for the semantic of launching its own
366      *         activity in the specified user profile.
367      *
368      * @see #startMainActivity(ComponentName, UserHandle)
369      */
getProfileSwitchingLabel(@onNull UserHandle userHandle)370     public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) {
371         verifyCanAccessUser(userHandle);
372 
373         final boolean isManagedProfile = mUserManager.isManagedProfile(userHandle.getIdentifier());
374         final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
375         final String callingAppLabel = getCallingApplicationLabel().toString();
376         return dpm.getResources().getString(
377                 getUpdatableProfileSwitchingLabelId(isManagedProfile),
378                 () -> getDefaultProfileSwitchingLabel(isManagedProfile, callingAppLabel),
379                 callingAppLabel);
380     }
381 
getCallingApplicationLabel()382     private CharSequence getCallingApplicationLabel() {
383         PackageManager pm = mContext.getPackageManager();
384         // If there is a label for the launcher intent, then use that as it is typically shorter.
385         // Otherwise, just use the top-level application name.
386         Intent launchIntent = pm.getLaunchIntentForPackage(mContext.getPackageName());
387         if (launchIntent == null) {
388             return getDefaultCallingApplicationLabel();
389         }
390         List<ResolveInfo> infos =
391                 pm.queryIntentActivities(
392                         launchIntent, PackageManager.ResolveInfoFlags.of(MATCH_DEFAULT_ONLY));
393         if (infos.size() > 0) {
394             return infos.get(0).loadLabel(pm);
395         }
396         return getDefaultCallingApplicationLabel();
397     }
398 
getDefaultCallingApplicationLabel()399     private CharSequence getDefaultCallingApplicationLabel() {
400         return mContext.getApplicationInfo()
401                 .loadSafeLabel(
402                         mContext.getPackageManager(),
403                         /* ellipsizeDip= */ 0,
404                         TextUtils.SAFE_STRING_FLAG_SINGLE_LINE
405                                 | TextUtils.SAFE_STRING_FLAG_TRIM);
406     }
407 
getUpdatableProfileSwitchingLabelId(boolean isManagedProfile)408     private String getUpdatableProfileSwitchingLabelId(boolean isManagedProfile) {
409         return isManagedProfile ? SWITCH_TO_WORK_LABEL : SWITCH_TO_PERSONAL_LABEL;
410     }
411 
getDefaultProfileSwitchingLabel(boolean isManagedProfile, String label)412     private String getDefaultProfileSwitchingLabel(boolean isManagedProfile, String label) {
413         final int stringRes = isManagedProfile
414                 ? R.string.managed_profile_app_label : R.string.user_owner_app_label;
415         return mResources.getString(stringRes, label);
416     }
417 
418 
419     /**
420      * Return a drawable that calling app can show to user for the semantic of profile switching --
421      * launching its own activity in specified user profile. For example, it may return a briefcase
422      * icon if the given user handle is the managed profile one.
423      *
424      * @param userHandle The UserHandle of the target profile, must be one of the users returned by
425      *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
426      *        be thrown.
427      * @return an icon that calling app can show user for the semantic of launching its own
428      *         activity in specified user profile.
429      *
430      * @see #startMainActivity(ComponentName, UserHandle)
431      */
getProfileSwitchingIconDrawable(@onNull UserHandle userHandle)432     public @NonNull Drawable getProfileSwitchingIconDrawable(@NonNull UserHandle userHandle) {
433         verifyCanAccessUser(userHandle);
434 
435         final boolean isManagedProfile =
436                 mUserManager.isManagedProfile(userHandle.getIdentifier());
437         if (isManagedProfile) {
438             return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
439                     userHandle, /* density= */ 0);
440         }
441         Drawable personalProfileIcon = UserIcons.getDefaultUserIcon(
442                 mResources, UserHandle.USER_SYSTEM,  /* light= */ true);
443         // Using the same colors as the managed profile icon.
444         int colorId = mContext.getResources().getConfiguration().isNightModeActive()
445                 ? R.color.profile_badge_1_dark
446                 : R.color.profile_badge_1;
447         // First set the color filter to null so that it does not override
448         // the tint.
449         personalProfileIcon.setColorFilter(null);
450         personalProfileIcon.setTint(mResources.getColor(colorId, /* theme= */ null));
451         return personalProfileIcon;
452     }
453 
454     /**
455      * Returns whether the calling package can request to navigate the user to
456      * the relevant settings page to request user consent to interact across profiles.
457      *
458      * <p>If {@code true}, the navigation intent can be obtained via {@link
459      * #createRequestInteractAcrossProfilesIntent()}. The package can then listen to {@link
460      * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts.
461      *
462      * <p>Specifically, returns whether the following are all true:
463      * <ul>
464      * <li>{@code UserManager#getProfileIdsExcludingHidden(int)} returns at least one other
465      * profile for the calling user.</li>
466      * <li>The calling app has requested
467      * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest.</li>
468      * <li>The calling app is not a profile owner within the profile group of the calling user.</li>
469      * </ul>
470      *
471      * <p>Note that in order for the user to be able to grant the consent, the requesting package
472      * must be allowlisted by the admin or the OEM and installed in the other profile. If this is
473      * not the case the user will be shown a message explaining why they can't grant the consent.
474      *
475      * <p>Note that user consent could already be granted if given a return value of {@code true}.
476      * The package's current ability to interact across profiles can be checked with {@link
477      * #canInteractAcrossProfiles()}.
478      *
479      * @return true if the calling package can request to interact across profiles.
480      */
canRequestInteractAcrossProfiles()481     public boolean canRequestInteractAcrossProfiles() {
482         try {
483             return mService.canRequestInteractAcrossProfiles(mContext.getPackageName());
484         } catch (RemoteException ex) {
485             throw ex.rethrowFromSystemServer();
486         }
487     }
488 
489     /**
490      * Returns whether the calling package can interact across profiles.
491 
492      * <p>Specifically, returns whether the following are all true:
493      * <ul>
494      * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li>
495      * <li>The user has previously consented to cross-profile communication for the calling
496      * package.</li>
497      * <li>The calling package has either been allowlisted by default by the OEM or has been
498      * explicitly allowlisted by the admin via
499      * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}.
500      * </li>
501      * </ul>
502      *
503      * <p>If {@code false}, the package's current ability to request user consent to interact across
504      * profiles can be checked with {@link #canRequestInteractAcrossProfiles()}. If {@code true},
505      * user consent can be obtained via {@link #createRequestInteractAcrossProfilesIntent()}. The
506      * package can then listen to {@link #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts.
507      *
508      * @return true if the calling package can interact across profiles.
509      * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the
510      * calling UID.
511      */
canInteractAcrossProfiles()512     public boolean canInteractAcrossProfiles() {
513         try {
514             return mService.canInteractAcrossProfiles(mContext.getPackageName());
515         } catch (RemoteException ex) {
516             throw ex.rethrowFromSystemServer();
517         }
518     }
519 
520     /**
521      * Returns an {@link Intent} to open the settings page that allows the user to decide whether
522      * the calling app can interact across profiles.
523      *
524      * <p>The method {@link #canRequestInteractAcrossProfiles()} must be returning {@code true}.
525      *
526      * <p>Note that the user may already have given consent and the app may already be able to
527      * interact across profiles, even if {@link #canRequestInteractAcrossProfiles()} is {@code
528      * true}. The current ability to interact across profiles is given by {@link
529      * #canInteractAcrossProfiles()}.
530      *
531      * @return an {@link Intent} to open the settings page that allows the user to decide whether
532      * the app can interact across profiles
533      *
534      * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the
535      * calling UID, or {@link #canRequestInteractAcrossProfiles()} is {@code false}.
536      */
createRequestInteractAcrossProfilesIntent()537     public @NonNull Intent createRequestInteractAcrossProfilesIntent() {
538         if (!canRequestInteractAcrossProfiles()) {
539             throw new SecurityException(
540                     "The calling package can not request to interact across profiles.");
541         }
542         final Intent settingsIntent = new Intent();
543         settingsIntent.setAction(Settings.ACTION_MANAGE_CROSS_PROFILE_ACCESS);
544         final Uri packageUri = Uri.parse("package:" + mContext.getPackageName());
545         settingsIntent.setData(packageUri);
546         return settingsIntent;
547     }
548 
549     /**
550      * Sets the app-op for {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} that is
551      * configurable by users in Settings. This configures it for the profile group of the calling
552      * package.
553      *
554      * <p>Before calling, check {@link #canConfigureInteractAcrossProfiles(String)} and do not call
555      * if it is {@code false}. If presenting a user interface, do not allow the user to configure
556      * the app-op in that case.
557      *
558      * <p>The underlying app-op {@link android.app.AppOpsManager#OP_INTERACT_ACROSS_PROFILES} should
559      * never be set directly. This method ensures that the app-op is kept in sync for the app across
560      * each user in the profile group and that those apps are sent a broadcast when their ability to
561      * interact across profiles changes.
562      *
563      * <p>This method should be used directly whenever a user's action results in a change in an
564      * app's ability to interact across profiles, as defined by the return value of {@link
565      * #canInteractAcrossProfiles()}. This includes user consent changes in Settings or during
566      * provisioning.
567      *
568      * <p>If other changes could have affected the app's ability to interact across profiles, as
569      * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the
570      * admin or OEM consent whitelists, then {@link #resetInteractAcrossProfilesAppOps(Collection,
571      * Set)} should be used.
572      *
573      * <p>If the caller does not have the {@link android.Manifest.permission
574      * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that
575      * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String,
576      * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}.
577      *
578      * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link
579      * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
580      *
581      * @hide
582      */
583     @RequiresPermission(
584             allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
585                     INTERACT_ACROSS_USERS})
586     @UserHandleAware(
587             enabledSinceTargetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
588             requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS)
setInteractAcrossProfilesAppOp(@onNull String packageName, @Mode int newMode)589     public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) {
590         try {
591             mService.setInteractAcrossProfilesAppOp(mContext.getUserId(), packageName, newMode);
592         } catch (RemoteException ex) {
593             throw ex.rethrowFromSystemServer();
594         }
595     }
596 
597     /**
598      * Returns whether the given package can have its ability to interact across profiles configured
599      * by the user. This means that every other condition to interact across profiles has been set.
600      *
601      * <p>This differs from {@link #canRequestInteractAcrossProfiles()} since it will not return
602      * {@code false} simply when the target profile is disabled.
603      *
604      * @hide
605      */
606     @TestApi
607     @UserHandleAware(
608             enabledSinceTargetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
609             requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS)
canConfigureInteractAcrossProfiles(@onNull String packageName)610     public boolean canConfigureInteractAcrossProfiles(@NonNull String packageName) {
611         try {
612             return mService.canConfigureInteractAcrossProfiles(mContext.getUserId(), packageName);
613         } catch (RemoteException ex) {
614             throw ex.rethrowFromSystemServer();
615         }
616     }
617 
618     /**
619      * Returns {@code true} if the given package has requested
620      * {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} and the user has at least one
621      * other profile in the same profile group.
622      *
623      * <p>This differs from {@link #canConfigureInteractAcrossProfiles(String)} since it will
624      * not return {@code false} if the app is not allowlisted or not installed in the other profile.
625      *
626      * <p>Note that platform-signed apps that are automatically granted the permission and are not
627      * allowlisted by the OEM will not be included in this list.
628      *
629      * @hide
630      */
631     @UserHandleAware(
632             enabledSinceTargetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
633             requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS)
canUserAttemptToConfigureInteractAcrossProfiles(String packageName)634     public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) {
635         try {
636             return mService.canUserAttemptToConfigureInteractAcrossProfiles(
637                     mContext.getUserId(), packageName);
638         } catch (RemoteException ex) {
639             throw ex.rethrowFromSystemServer();
640         }
641     }
642     /**
643      * For each of the packages defined in {@code previousCrossProfilePackages} but not included in
644      * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission
645      * #INTERACT_ACROSS_PROFILES} back to its default value if it can no longer be configured by
646      * users in Settings, as defined by {@link #canConfigureInteractAcrossProfiles(String)}.
647      *
648      * <p>This method should be used whenever an app's ability to interact across profiles could
649      * have changed as a result of non-user actions, such as changes to admin or OEM consent
650      * whitelists.
651      *
652      * <p>If the caller does not have the {@link android.Manifest.permission
653      * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that
654      * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String,
655      * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}.
656      *
657      * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link
658      * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
659      *
660      * @hide
661      */
662     @RequiresPermission(
663             allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
664                     INTERACT_ACROSS_USERS})
665     @UserHandleAware(
666             enabledSinceTargetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
667             requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS)
resetInteractAcrossProfilesAppOps( @onNull Collection<String> previousCrossProfilePackages, @NonNull Set<String> newCrossProfilePackages)668     public void resetInteractAcrossProfilesAppOps(
669             @NonNull Collection<String> previousCrossProfilePackages,
670             @NonNull Set<String> newCrossProfilePackages) {
671         if (previousCrossProfilePackages.isEmpty()) {
672             return;
673         }
674         final List<String> unsetCrossProfilePackages =
675                 previousCrossProfilePackages.stream()
676                         .filter(packageName -> !newCrossProfilePackages.contains(packageName))
677                         .collect(Collectors.toList());
678         if (unsetCrossProfilePackages.isEmpty()) {
679             return;
680         }
681         try {
682             mService.resetInteractAcrossProfilesAppOps(
683                     mContext.getUserId(), unsetCrossProfilePackages);
684         } catch (RemoteException ex) {
685             throw ex.rethrowFromSystemServer();
686         }
687     }
688 
689     /**
690      * Clears the app-op for {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} back to
691      * its default value for every package on the device.
692      *
693      * <p>This method can be used to ensure that app-op state is not left around on existing users
694      * for previously-configured profiles.
695      *
696      * <p>If the caller does not have the {@link android.Manifest.permission
697      * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that
698      * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String,
699      * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}.
700      *
701      * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link
702      * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
703      *
704      * @hide
705      */
706     @RequiresPermission(
707             allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
708                     INTERACT_ACROSS_USERS})
709     @UserHandleAware(
710             enabledSinceTargetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
711             requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS)
clearInteractAcrossProfilesAppOps()712     public void clearInteractAcrossProfilesAppOps() {
713         try {
714             mService.clearInteractAcrossProfilesAppOps(mContext.getUserId());
715         } catch (RemoteException ex) {
716             throw ex.rethrowFromSystemServer();
717         }
718     }
719 
720     /**
721      * A validation method to check that the methods in this class are only being applied to user
722      * handles returned by {@link #getTargetUserProfiles()}. As this is run client-side for
723      * input validation purposes, this should never replace a real security check service-side.
724      */
verifyCanAccessUser(UserHandle userHandle)725     private void verifyCanAccessUser(UserHandle userHandle) {
726         if (!getTargetUserProfiles().contains(userHandle)) {
727             throw new SecurityException("Not allowed to access " + userHandle);
728         }
729     }
730 }
731