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