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