1 /* 2 * Copyright (C) 2018 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 17 package com.android.permissioncontroller.role.model; 18 19 import android.app.ActivityManager; 20 import android.app.role.RoleManager; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.PackageManager; 26 import android.content.pm.SharedLibraryInfo; 27 import android.content.pm.Signature; 28 import android.content.res.Resources; 29 import android.os.Build; 30 import android.os.Process; 31 import android.os.UserHandle; 32 import android.text.TextUtils; 33 import android.util.ArrayMap; 34 import android.util.ArraySet; 35 import android.util.Log; 36 37 import androidx.annotation.NonNull; 38 import androidx.annotation.Nullable; 39 import androidx.annotation.StringRes; 40 import androidx.preference.Preference; 41 42 import com.android.modules.utils.build.SdkLevel; 43 import com.android.permissioncontroller.Constants; 44 import com.android.permissioncontroller.permission.utils.CollectionUtils; 45 import com.android.permissioncontroller.permission.utils.Utils; 46 import com.android.permissioncontroller.role.ui.TwoTargetPreference; 47 import com.android.permissioncontroller.role.utils.PackageUtils; 48 import com.android.permissioncontroller.role.utils.RoleManagerCompat; 49 import com.android.permissioncontroller.role.utils.UserUtils; 50 51 import java.util.ArrayList; 52 import java.util.Collections; 53 import java.util.List; 54 import java.util.Objects; 55 56 /** 57 * Specifies a role and its properties. 58 * <p> 59 * A role is a unique name within the system associated with certain privileges. There can be 60 * multiple applications qualifying for a role, but only a subset of them can become role holders. 61 * To qualify for a role, an application must meet certain requirements, including defining certain 62 * components in its manifest. Then the application will need user consent to become the role 63 * holder. 64 * <p> 65 * Upon becoming a role holder, the application may be granted certain permissions, have certain 66 * app ops set to certain modes and certain {@code Activity} components configured as preferred for 67 * certain {@code Intent} actions. When an application loses its role, these privileges will also be 68 * revoked. 69 * 70 * @see android.app.role.RoleManager 71 */ 72 public class Role { 73 74 private static final String LOG_TAG = Role.class.getSimpleName(); 75 76 private static final boolean DEBUG = false; 77 78 private static final String PACKAGE_NAME_ANDROID_SYSTEM = "android"; 79 80 private static final String DEFAULT_HOLDER_SEPARATOR = ";"; 81 82 private static final String CERTIFICATE_SEPARATOR = ":"; 83 84 /** 85 * The name of this role. Must be unique. 86 */ 87 @NonNull 88 private final String mName; 89 90 /** 91 * Whether this role allows bypassing role holder qualification. 92 */ 93 private final boolean mAllowBypassingQualification; 94 95 /** 96 * The behavior of this role. 97 */ 98 @Nullable 99 private final RoleBehavior mBehavior; 100 101 @Nullable 102 private final String mDefaultHoldersResourceName; 103 104 /** 105 * The string resource for the description of this role. 106 */ 107 @StringRes 108 private final int mDescriptionResource; 109 110 /** 111 * Whether this role is exclusive, i.e. allows at most one holder. 112 */ 113 private final boolean mExclusive; 114 115 /** 116 * Whether this role should fall back to the default holder. 117 */ 118 private final boolean mFallBackToDefaultHolder; 119 120 /** 121 * The string resource for the label of this role. 122 */ 123 @StringRes 124 private final int mLabelResource; 125 126 /** 127 * The minimum SDK version for this role to be available. 128 */ 129 private final int mMinSdkVersion; 130 131 /** 132 * Whether this role should override user's choice about privileges when granting. 133 */ 134 private final boolean mOverrideUserWhenGranting; 135 136 /** 137 * The string resource for the request description of this role, shown below the selected app in 138 * the request role dialog. 139 */ 140 @StringRes 141 private final int mRequestDescriptionResource; 142 143 /** 144 * The string resource for the request title of this role, shown as the title of the request 145 * role dialog. 146 */ 147 @StringRes 148 private final int mRequestTitleResource; 149 150 /** 151 * Whether this role is requestable by applications with 152 * {@link android.app.role.RoleManager#createRequestRoleIntent(String)}. 153 */ 154 private final boolean mRequestable; 155 156 /** 157 * The string resource for search keywords of this role, in addition to the label of this role, 158 * if it's non-zero. 159 */ 160 @StringRes 161 private final int mSearchKeywordsResource; 162 163 /** 164 * The string resource for the short label of this role, currently used when in a list of roles. 165 */ 166 @StringRes 167 private final int mShortLabelResource; 168 169 /** 170 * Whether the UI for this role will show the "None" item. Only valid if this role is 171 * {@link #mExclusive exclusive}, and {@link #getFallbackHolder(Context)} should also return 172 * empty to allow actually selecting "None". 173 */ 174 private final boolean mShowNone; 175 176 /** 177 * Whether this role is static, i.e. the role will always be assigned to its default holders. 178 */ 179 private final boolean mStatic; 180 181 /** 182 * Whether this role only accepts system apps as its holders. 183 */ 184 private final boolean mSystemOnly; 185 186 /** 187 * Whether this role is visible to user. 188 */ 189 private final boolean mVisible; 190 191 /** 192 * The required components for an application to qualify for this role. 193 */ 194 @NonNull 195 private final List<RequiredComponent> mRequiredComponents; 196 197 /** 198 * The permissions to be granted by this role. 199 */ 200 @NonNull 201 private final List<Permission> mPermissions; 202 203 /** 204 * The app op permissions to be granted by this role. 205 */ 206 @NonNull 207 private final List<String> mAppOpPermissions; 208 209 /** 210 * The app ops to be set to allowed by this role. 211 */ 212 @NonNull 213 private final List<AppOp> mAppOps; 214 215 /** 216 * The set of preferred {@code Activity} configurations to be configured by this role. 217 */ 218 @NonNull 219 private final List<PreferredActivity> mPreferredActivities; 220 Role(@onNull String name, boolean allowBypassingQualification, @Nullable RoleBehavior behavior, @Nullable String defaultHoldersResourceName, @StringRes int descriptionResource, boolean exclusive, boolean fallBackToDefaultHolder, @StringRes int labelResource, int minSdkVersion, boolean overrideUserWhenGranting, @StringRes int requestDescriptionResource, @StringRes int requestTitleResource, boolean requestable, @StringRes int searchKeywordsResource, @StringRes int shortLabelResource, boolean showNone, boolean statik, boolean systemOnly, boolean visible, @NonNull List<RequiredComponent> requiredComponents, @NonNull List<Permission> permissions, @NonNull List<String> appOpPermissions, @NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities)221 public Role(@NonNull String name, boolean allowBypassingQualification, 222 @Nullable RoleBehavior behavior, @Nullable String defaultHoldersResourceName, 223 @StringRes int descriptionResource, boolean exclusive, boolean fallBackToDefaultHolder, 224 @StringRes int labelResource, int minSdkVersion, boolean overrideUserWhenGranting, 225 @StringRes int requestDescriptionResource, @StringRes int requestTitleResource, 226 boolean requestable, @StringRes int searchKeywordsResource, 227 @StringRes int shortLabelResource, boolean showNone, boolean statik, boolean systemOnly, 228 boolean visible, @NonNull List<RequiredComponent> requiredComponents, 229 @NonNull List<Permission> permissions, @NonNull List<String> appOpPermissions, 230 @NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities) { 231 mName = name; 232 mAllowBypassingQualification = allowBypassingQualification; 233 mBehavior = behavior; 234 mDefaultHoldersResourceName = defaultHoldersResourceName; 235 mDescriptionResource = descriptionResource; 236 mExclusive = exclusive; 237 mFallBackToDefaultHolder = fallBackToDefaultHolder; 238 mLabelResource = labelResource; 239 mMinSdkVersion = minSdkVersion; 240 mOverrideUserWhenGranting = overrideUserWhenGranting; 241 mRequestDescriptionResource = requestDescriptionResource; 242 mRequestTitleResource = requestTitleResource; 243 mRequestable = requestable; 244 mSearchKeywordsResource = searchKeywordsResource; 245 mShortLabelResource = shortLabelResource; 246 mShowNone = showNone; 247 mStatic = statik; 248 mSystemOnly = systemOnly; 249 mVisible = visible; 250 mRequiredComponents = requiredComponents; 251 mPermissions = permissions; 252 mAppOpPermissions = appOpPermissions; 253 mAppOps = appOps; 254 mPreferredActivities = preferredActivities; 255 } 256 257 @NonNull getName()258 public String getName() { 259 return mName; 260 } 261 262 @Nullable getBehavior()263 public RoleBehavior getBehavior() { 264 return mBehavior; 265 } 266 267 @StringRes getDescriptionResource()268 public int getDescriptionResource() { 269 return mDescriptionResource; 270 } 271 isExclusive()272 public boolean isExclusive() { 273 return mExclusive; 274 } 275 276 @StringRes getLabelResource()277 public int getLabelResource() { 278 return mLabelResource; 279 } 280 281 @StringRes getRequestDescriptionResource()282 public int getRequestDescriptionResource() { 283 return mRequestDescriptionResource; 284 } 285 286 @StringRes getRequestTitleResource()287 public int getRequestTitleResource() { 288 return mRequestTitleResource; 289 } 290 isRequestable()291 public boolean isRequestable() { 292 return mRequestable; 293 } 294 295 @StringRes getSearchKeywordsResource()296 public int getSearchKeywordsResource() { 297 return mSearchKeywordsResource; 298 } 299 300 @StringRes getShortLabelResource()301 public int getShortLabelResource() { 302 return mShortLabelResource; 303 } 304 305 /** 306 * @see #mOverrideUserWhenGranting 307 */ shouldOverrideUserWhenGranting()308 public boolean shouldOverrideUserWhenGranting() { 309 return mOverrideUserWhenGranting; 310 } 311 312 /** 313 * @see #mShowNone 314 */ shouldShowNone()315 public boolean shouldShowNone() { 316 return mShowNone; 317 } 318 isVisible()319 public boolean isVisible() { 320 return mVisible; 321 } 322 323 @NonNull getRequiredComponents()324 public List<RequiredComponent> getRequiredComponents() { 325 return mRequiredComponents; 326 } 327 328 @NonNull getPermissions()329 public List<Permission> getPermissions() { 330 return mPermissions; 331 } 332 333 @NonNull getAppOpPermissions()334 public List<String> getAppOpPermissions() { 335 return mAppOpPermissions; 336 } 337 338 @NonNull getAppOps()339 public List<AppOp> getAppOps() { 340 return mAppOps; 341 } 342 343 @NonNull getPreferredActivities()344 public List<PreferredActivity> getPreferredActivities() { 345 return mPreferredActivities; 346 } 347 348 /** 349 * Callback when this role is added to the system for the first time. 350 * 351 * @param context the {@code Context} to retrieve system services 352 */ onRoleAdded(@onNull Context context)353 public void onRoleAdded(@NonNull Context context) { 354 if (mBehavior != null) { 355 mBehavior.onRoleAdded(this, context); 356 } 357 } 358 359 /** 360 * Check whether this role is available. 361 * 362 * @param user the user to check for 363 * @param context the {@code Context} to retrieve system services 364 * 365 * @return whether this role is available. 366 */ isAvailableAsUser(@onNull UserHandle user, @NonNull Context context)367 public boolean isAvailableAsUser(@NonNull UserHandle user, @NonNull Context context) { 368 if (!isAvailableBySdkVersion()) { 369 return false; 370 } 371 if (mBehavior != null) { 372 return mBehavior.isAvailableAsUser(this, user, context); 373 } 374 return true; 375 } 376 377 /** 378 * Check whether this role is available based on SDK version. 379 * 380 * @return whether this role is available based on SDK version 381 */ isAvailableBySdkVersion()382 boolean isAvailableBySdkVersion() { 383 // Workaround to match the value 33+ for T+ in roles.xml before SDK finalization. 384 if (mMinSdkVersion >= 33) { 385 return SdkLevel.isAtLeastT(); 386 } else { 387 return Build.VERSION.SDK_INT >= mMinSdkVersion; 388 } 389 } 390 391 /** 392 * Check whether this role is available, for current user. 393 * 394 * @param context the {@code Context} to retrieve system services 395 * 396 * @return whether this role is available. 397 */ isAvailable(@onNull Context context)398 public boolean isAvailable(@NonNull Context context) { 399 return isAvailableAsUser(Process.myUserHandle(), context); 400 } 401 isStatic()402 public boolean isStatic() { 403 return mStatic; 404 } 405 406 /** 407 * Get the default holders of this role, which will be added when the role is added for the 408 * first time. 409 * 410 * @param context the {@code Context} to retrieve system services 411 * 412 * @return the list of package names of the default holders 413 */ 414 @NonNull getDefaultHolders(@onNull Context context)415 public List<String> getDefaultHolders(@NonNull Context context) { 416 if (mDefaultHoldersResourceName == null) { 417 if (mBehavior != null) { 418 return mBehavior.getDefaultHolders(this, context); 419 } 420 return Collections.emptyList(); 421 } 422 423 Resources resources = context.getResources(); 424 int resourceId = resources.getIdentifier(mDefaultHoldersResourceName, "string", "android"); 425 if (resourceId == 0) { 426 Log.w(LOG_TAG, "Cannot find resource for default holder: " 427 + mDefaultHoldersResourceName); 428 return Collections.emptyList(); 429 } 430 431 String defaultHolders; 432 try { 433 defaultHolders = resources.getString(resourceId); 434 } catch (Resources.NotFoundException e) { 435 Log.w(LOG_TAG, "Cannot get resource for default holder: " + mDefaultHoldersResourceName, 436 e); 437 return Collections.emptyList(); 438 } 439 if (TextUtils.isEmpty(defaultHolders)) { 440 return Collections.emptyList(); 441 } 442 443 if (isExclusive()) { 444 String packageName = getQualifiedDefaultHolderPackageName(defaultHolders, context); 445 if (packageName == null) { 446 return Collections.emptyList(); 447 } 448 return Collections.singletonList(packageName); 449 } else { 450 List<String> packageNames = new ArrayList<>(); 451 for (String defaultHolder : defaultHolders.split(DEFAULT_HOLDER_SEPARATOR)) { 452 String packageName = getQualifiedDefaultHolderPackageName(defaultHolders, context); 453 if (packageName != null) { 454 packageNames.add(packageName); 455 } 456 } 457 return packageNames; 458 } 459 } 460 461 @Nullable getQualifiedDefaultHolderPackageName(@onNull String defaultHolder, @NonNull Context context)462 private String getQualifiedDefaultHolderPackageName(@NonNull String defaultHolder, 463 @NonNull Context context) { 464 String packageName; 465 byte[] certificate; 466 int certificateSeparatorIndex = defaultHolder.indexOf(CERTIFICATE_SEPARATOR); 467 if (certificateSeparatorIndex != -1) { 468 packageName = defaultHolder.substring(0, certificateSeparatorIndex); 469 String certificateString = defaultHolder.substring(certificateSeparatorIndex + 1); 470 try { 471 certificate = new Signature(certificateString).toByteArray(); 472 } catch (IllegalArgumentException e) { 473 Log.w(LOG_TAG, "Cannot parse signing certificate: " + defaultHolder, e); 474 return null; 475 } 476 } else { 477 packageName = defaultHolder; 478 certificate = null; 479 } 480 481 if (certificate != null) { 482 PackageManager packageManager = context.getPackageManager(); 483 if (!packageManager.hasSigningCertificate(packageName, certificate, 484 PackageManager.CERT_INPUT_SHA256)) { 485 Log.w(LOG_TAG, "Default holder doesn't have required signing certificate: " 486 + defaultHolder); 487 return null; 488 } 489 } else { 490 ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, context); 491 if (applicationInfo == null) { 492 Log.w(LOG_TAG, "Cannot get ApplicationInfo for default holder: " + packageName); 493 return null; 494 } 495 if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { 496 Log.w(LOG_TAG, "Default holder didn't specify a signing certificate and isn't a" 497 + " system app: " + packageName); 498 return null; 499 } 500 } 501 502 return packageName; 503 } 504 505 /** 506 * Get the fallback holder of this role, which will be added whenever there are no role holders. 507 * <p> 508 * Should return {@code null} if this role {@link #mShowNone shows a "None" item}. 509 * 510 * @param context the {@code Context} to retrieve system services 511 * 512 * @return the package name of the fallback holder, or {@code null} if none 513 */ 514 @Nullable getFallbackHolder(@onNull Context context)515 public String getFallbackHolder(@NonNull Context context) { 516 if (isNoneHolderSelected(context)) { 517 return null; 518 } 519 if (mFallBackToDefaultHolder) { 520 return CollectionUtils.firstOrNull(getDefaultHolders(context)); 521 } 522 if (mBehavior != null) { 523 return mBehavior.getFallbackHolder(this, context); 524 } 525 return null; 526 } 527 528 /** 529 * Check whether this role should be visible to user. 530 * 531 * @param user the user to check for 532 * @param context the {@code Context} to retrieve system services 533 * 534 * @return whether this role should be visible to user 535 */ isVisibleAsUser(@onNull UserHandle user, @NonNull Context context)536 public boolean isVisibleAsUser(@NonNull UserHandle user, @NonNull Context context) { 537 return mVisible && (mBehavior == null || mBehavior.isVisibleAsUser(this, user, context)); 538 } 539 540 /** 541 * Check whether this role should be visible to user, for current user. 542 * 543 * @param context the {@code Context} to retrieve system services 544 * 545 * @return whether this role should be visible to user. 546 */ isVisible(@onNull Context context)547 public boolean isVisible(@NonNull Context context) { 548 return isVisibleAsUser(Process.myUserHandle(), context); 549 } 550 551 /** 552 * Get the {@link Intent} to manage this role, or {@code null} to use the default UI. 553 * 554 * @param user the user to manage this role for 555 * @param context the {@code Context} to retrieve system services 556 * 557 * @return the {@link Intent} to manage this role, or {@code null} to use the default UI. 558 */ 559 @Nullable getManageIntentAsUser(@onNull UserHandle user, @NonNull Context context)560 public Intent getManageIntentAsUser(@NonNull UserHandle user, @NonNull Context context) { 561 if (mBehavior != null) { 562 return mBehavior.getManageIntentAsUser(this, user, context); 563 } 564 return null; 565 } 566 567 /** 568 * Prepare a {@link Preference} for this role. 569 * 570 * @param preference the {@link Preference} for this role 571 * @param user the user for this role 572 * @param context the {@code Context} to retrieve system services 573 */ preparePreferenceAsUser(@onNull TwoTargetPreference preference, @NonNull UserHandle user, @NonNull Context context)574 public void preparePreferenceAsUser(@NonNull TwoTargetPreference preference, 575 @NonNull UserHandle user, @NonNull Context context) { 576 if (mBehavior != null) { 577 mBehavior.preparePreferenceAsUser(this, preference, user, context); 578 } 579 } 580 581 /** 582 * Check whether a qualifying application should be visible to user. 583 * 584 * @param applicationInfo the {@link ApplicationInfo} for the application 585 * @param user the user for the application 586 * @param context the {@code Context} to retrieve system services 587 * 588 * @return whether the qualifying application should be visible to user 589 */ isApplicationVisibleAsUser(@onNull ApplicationInfo applicationInfo, @NonNull UserHandle user, @NonNull Context context)590 public boolean isApplicationVisibleAsUser(@NonNull ApplicationInfo applicationInfo, 591 @NonNull UserHandle user, @NonNull Context context) { 592 if (mBehavior != null) { 593 return mBehavior.isApplicationVisibleAsUser(this, applicationInfo, user, context); 594 } 595 return true; 596 } 597 598 /** 599 * Prepare a {@link Preference} for an application. 600 * 601 * @param preference the {@link Preference} for the application 602 * @param applicationInfo the {@link ApplicationInfo} for the application 603 * @param user the user for the application 604 * @param context the {@code Context} to retrieve system services 605 */ prepareApplicationPreferenceAsUser(@onNull Preference preference, @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, @NonNull Context context)606 public void prepareApplicationPreferenceAsUser(@NonNull Preference preference, 607 @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, 608 @NonNull Context context) { 609 if (mBehavior != null) { 610 mBehavior.prepareApplicationPreferenceAsUser(this, preference, applicationInfo, user, 611 context); 612 } 613 } 614 615 /** 616 * Get the confirmation message for adding an application as a holder of this role. 617 * 618 * @param packageName the package name of the application to get confirmation message for 619 * @param context the {@code Context} to retrieve system services 620 * 621 * @return the confirmation message, or {@code null} if no confirmation is needed 622 */ 623 @Nullable getConfirmationMessage(@onNull String packageName, @NonNull Context context)624 public CharSequence getConfirmationMessage(@NonNull String packageName, 625 @NonNull Context context) { 626 if (mBehavior != null) { 627 return mBehavior.getConfirmationMessage(this, packageName, context); 628 } 629 return null; 630 } 631 632 /** 633 * Check whether this role is allowed to bypass qualification, if enabled globally. 634 * 635 * @param context the {@code Context} to retrieve system services 636 * 637 * @return whether this role is allowed to bypass qualification 638 */ shouldAllowBypassingQualification(@onNull Context context)639 public boolean shouldAllowBypassingQualification(@NonNull Context context) { 640 if (mBehavior != null) { 641 Boolean allowBypassingQualification = mBehavior.shouldAllowBypassingQualification(this, 642 context); 643 if (allowBypassingQualification != null) { 644 return allowBypassingQualification; 645 } 646 } 647 return mAllowBypassingQualification; 648 } 649 650 /** 651 * Check whether a package is qualified for this role, i.e. whether it contains all the required 652 * components (plus meeting some other general restrictions). 653 * 654 * @param packageName the package name to check for 655 * @param context the {@code Context} to retrieve system services 656 * 657 * @return whether the package is qualified for a role 658 */ isPackageQualified(@onNull String packageName, @NonNull Context context)659 public boolean isPackageQualified(@NonNull String packageName, @NonNull Context context) { 660 RoleManager roleManager = context.getSystemService(RoleManager.class); 661 if (shouldAllowBypassingQualification(context) 662 && RoleManagerCompat.isBypassingRoleQualification(roleManager)) { 663 return true; 664 } 665 666 ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, context); 667 if (applicationInfo == null) { 668 Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName); 669 return false; 670 } 671 if (!isPackageMinimallyQualifiedAsUser(applicationInfo, Process.myUserHandle(), context)) { 672 return false; 673 } 674 675 if (mBehavior != null) { 676 Boolean isPackageQualified = mBehavior.isPackageQualified(this, packageName, context); 677 if (isPackageQualified != null) { 678 return isPackageQualified; 679 } 680 } 681 682 int requiredComponentsSize = mRequiredComponents.size(); 683 for (int i = 0; i < requiredComponentsSize; i++) { 684 RequiredComponent requiredComponent = mRequiredComponents.get(i); 685 686 if (!requiredComponent.isRequired(applicationInfo)) { 687 continue; 688 } 689 690 if (requiredComponent.getQualifyingComponentForPackage(packageName, context) == null) { 691 Log.i(LOG_TAG, packageName + " not qualified for " + mName 692 + " due to missing " + requiredComponent); 693 return false; 694 } 695 } 696 697 if (mStatic && !getDefaultHolders(context).contains(packageName)) { 698 return false; 699 } 700 701 return true; 702 } 703 704 /** 705 * Get the list of packages that are qualified for this role, i.e. packages containing all the 706 * required components (plus meeting some other general restrictions). 707 * 708 * @param user the user to get the qualifying packages. 709 * @param context the {@code Context} to retrieve system services 710 * 711 * @return the list of packages that are qualified for this role 712 */ 713 @NonNull getQualifyingPackagesAsUser(@onNull UserHandle user, @NonNull Context context)714 public List<String> getQualifyingPackagesAsUser(@NonNull UserHandle user, 715 @NonNull Context context) { 716 List<String> qualifyingPackages = null; 717 718 if (mBehavior != null) { 719 qualifyingPackages = mBehavior.getQualifyingPackagesAsUser(this, user, context); 720 } 721 722 ArrayMap<String, ApplicationInfo> packageApplicationInfoMap = new ArrayMap<>(); 723 if (qualifyingPackages == null) { 724 ArrayMap<String, ArraySet<RequiredComponent>> packageRequiredComponentsMap = 725 new ArrayMap<>(); 726 int requiredComponentsSize = mRequiredComponents.size(); 727 for (int requiredComponentsIndex = 0; requiredComponentsIndex < requiredComponentsSize; 728 requiredComponentsIndex++) { 729 RequiredComponent requiredComponent = mRequiredComponents.get( 730 requiredComponentsIndex); 731 732 if (!requiredComponent.isAvailable()) { 733 continue; 734 } 735 736 // This returns at most one component per package. 737 List<ComponentName> qualifyingComponents = 738 requiredComponent.getQualifyingComponentsAsUser(user, context); 739 int qualifyingComponentsSize = qualifyingComponents.size(); 740 for (int qualifyingComponentsIndex = 0; 741 qualifyingComponentsIndex < qualifyingComponentsSize; 742 ++qualifyingComponentsIndex) { 743 ComponentName componentName = qualifyingComponents.get( 744 qualifyingComponentsIndex); 745 746 String packageName = componentName.getPackageName(); 747 ArraySet<RequiredComponent> packageRequiredComponents = 748 packageRequiredComponentsMap.get(packageName); 749 if (packageRequiredComponents == null) { 750 packageRequiredComponents = new ArraySet<>(); 751 packageRequiredComponentsMap.put(packageName, packageRequiredComponents); 752 } 753 packageRequiredComponents.add(requiredComponent); 754 } 755 } 756 757 qualifyingPackages = new ArrayList<>(); 758 int packageRequiredComponentsMapSize = packageRequiredComponentsMap.size(); 759 for (int packageRequiredComponentsMapIndex = 0; 760 packageRequiredComponentsMapIndex < packageRequiredComponentsMapSize; 761 packageRequiredComponentsMapIndex++) { 762 String packageName = packageRequiredComponentsMap.keyAt( 763 packageRequiredComponentsMapIndex); 764 ArraySet<RequiredComponent> packageRequiredComponents = 765 packageRequiredComponentsMap.valueAt(packageRequiredComponentsMapIndex); 766 767 ApplicationInfo applicationInfo = packageApplicationInfoMap.get(packageName); 768 if (applicationInfo == null) { 769 applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName, user, 770 context); 771 if (applicationInfo == null) { 772 Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName 773 + ", user: " + user.getIdentifier()); 774 continue; 775 } 776 packageApplicationInfoMap.put(packageName, applicationInfo); 777 } 778 779 boolean hasAllRequiredComponents = true; 780 for (int requiredComponentsIndex = 0; 781 requiredComponentsIndex < requiredComponentsSize; 782 requiredComponentsIndex++) { 783 RequiredComponent requiredComponent = mRequiredComponents.get( 784 requiredComponentsIndex); 785 786 if (!requiredComponent.isRequired(applicationInfo)) { 787 continue; 788 } 789 790 if (!packageRequiredComponents.contains(requiredComponent)) { 791 hasAllRequiredComponents = false; 792 break; 793 } 794 } 795 796 if (hasAllRequiredComponents) { 797 qualifyingPackages.add(packageName); 798 } 799 } 800 } 801 802 int qualifyingPackagesSize = qualifyingPackages.size(); 803 for (int i = 0; i < qualifyingPackagesSize; ) { 804 String packageName = qualifyingPackages.get(i); 805 806 ApplicationInfo applicationInfo = packageApplicationInfoMap.get(packageName); 807 if (applicationInfo == null) { 808 applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName, user, 809 context); 810 if (applicationInfo == null) { 811 Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName 812 + ", user: " + user.getIdentifier()); 813 continue; 814 } 815 packageApplicationInfoMap.put(packageName, applicationInfo); 816 } 817 818 if (!isPackageMinimallyQualifiedAsUser(applicationInfo, user, context)) { 819 qualifyingPackages.remove(i); 820 qualifyingPackagesSize--; 821 } else { 822 i++; 823 } 824 } 825 826 return qualifyingPackages; 827 } 828 isPackageMinimallyQualifiedAsUser(@onNull ApplicationInfo applicationInfo, @NonNull UserHandle user, @NonNull Context context)829 private boolean isPackageMinimallyQualifiedAsUser(@NonNull ApplicationInfo applicationInfo, 830 @NonNull UserHandle user, 831 @NonNull Context context) { 832 String packageName = applicationInfo.packageName; 833 if (Objects.equals(packageName, PACKAGE_NAME_ANDROID_SYSTEM)) { 834 return false; 835 } 836 837 if (mSystemOnly && (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { 838 return false; 839 } 840 841 if (!applicationInfo.enabled) { 842 return false; 843 } 844 845 if (applicationInfo.isInstantApp()) { 846 return false; 847 } 848 849 PackageManager userPackageManager = UserUtils.getUserContext(context, user) 850 .getPackageManager(); 851 List<SharedLibraryInfo> declaredLibraries = userPackageManager.getDeclaredSharedLibraries( 852 packageName, 0); 853 final int libCount = declaredLibraries.size(); 854 for (int i = 0; i < libCount; i++) { 855 SharedLibraryInfo sharedLibrary = declaredLibraries.get(i); 856 if (sharedLibrary.getType() != SharedLibraryInfo.TYPE_DYNAMIC) { 857 return false; 858 } 859 } 860 861 return true; 862 } 863 864 /** 865 * Grant this role to an application. 866 * 867 * @param packageName the package name of the application to be granted this role to 868 * @param dontKillApp whether this application should not be killed despite changes 869 * @param overrideUser whether to override user when granting privileges 870 * @param context the {@code Context} to retrieve system services 871 */ grant(@onNull String packageName, boolean dontKillApp, boolean overrideUser, @NonNull Context context)872 public void grant(@NonNull String packageName, boolean dontKillApp, 873 boolean overrideUser, @NonNull Context context) { 874 boolean permissionOrAppOpChanged = Permissions.grant(packageName, 875 Permissions.filterBySdkVersion(mPermissions), 876 SdkLevel.isAtLeastS() ? !mSystemOnly : true, overrideUser, true, false, false, 877 context); 878 879 int appOpPermissionsSize = mAppOpPermissions.size(); 880 for (int i = 0; i < appOpPermissionsSize; i++) { 881 String appOpPermission = mAppOpPermissions.get(i); 882 AppOpPermissions.grant(packageName, appOpPermission, overrideUser, context); 883 } 884 885 int appOpsSize = mAppOps.size(); 886 for (int i = 0; i < appOpsSize; i++) { 887 AppOp appOp = mAppOps.get(i); 888 appOp.grant(packageName, context); 889 } 890 891 int preferredActivitiesSize = mPreferredActivities.size(); 892 for (int i = 0; i < preferredActivitiesSize; i++) { 893 PreferredActivity preferredActivity = mPreferredActivities.get(i); 894 preferredActivity.configure(packageName, context); 895 } 896 897 if (mBehavior != null) { 898 mBehavior.grant(this, packageName, context); 899 } 900 901 if (!dontKillApp && permissionOrAppOpChanged && !Permissions.isRuntimePermissionsSupported( 902 packageName, context)) { 903 killApp(packageName, context); 904 } 905 } 906 907 /** 908 * Revoke this role from an application. 909 * 910 * @param packageName the package name of the application to be granted this role to 911 * @param dontKillApp whether this application should not be killed despite changes 912 * @param overrideSystemFixedPermissions whether system-fixed permissions can be revoked 913 * @param context the {@code Context} to retrieve system services 914 */ revoke(@onNull String packageName, boolean dontKillApp, boolean overrideSystemFixedPermissions, @NonNull Context context)915 public void revoke(@NonNull String packageName, boolean dontKillApp, 916 boolean overrideSystemFixedPermissions, @NonNull Context context) { 917 RoleManager roleManager = context.getSystemService(RoleManager.class); 918 List<String> otherRoleNames = roleManager.getHeldRolesFromController(packageName); 919 otherRoleNames.remove(mName); 920 921 List<String> permissionsToRevoke = Permissions.filterBySdkVersion(mPermissions); 922 ArrayMap<String, Role> roles = Roles.get(context); 923 int otherRoleNamesSize = otherRoleNames.size(); 924 for (int i = 0; i < otherRoleNamesSize; i++) { 925 String roleName = otherRoleNames.get(i); 926 Role role = roles.get(roleName); 927 permissionsToRevoke.removeAll(Permissions.filterBySdkVersion(role.mPermissions)); 928 } 929 930 boolean permissionOrAppOpChanged = Permissions.revoke(packageName, permissionsToRevoke, 931 true, false, overrideSystemFixedPermissions, context); 932 933 List<String> appOpPermissionsToRevoke = new ArrayList<>(mAppOpPermissions); 934 for (int i = 0; i < otherRoleNamesSize; i++) { 935 String roleName = otherRoleNames.get(i); 936 Role role = roles.get(roleName); 937 appOpPermissionsToRevoke.removeAll(role.mAppOpPermissions); 938 } 939 int appOpPermissionsSize = appOpPermissionsToRevoke.size(); 940 for (int i = 0; i < appOpPermissionsSize; i++) { 941 String appOpPermission = appOpPermissionsToRevoke.get(i); 942 AppOpPermissions.revoke(packageName, appOpPermission, context); 943 } 944 945 List<AppOp> appOpsToRevoke = new ArrayList<>(mAppOps); 946 for (int i = 0; i < otherRoleNamesSize; i++) { 947 String roleName = otherRoleNames.get(i); 948 Role role = roles.get(roleName); 949 appOpsToRevoke.removeAll(role.mAppOps); 950 } 951 int appOpsSize = appOpsToRevoke.size(); 952 for (int i = 0; i < appOpsSize; i++) { 953 AppOp appOp = appOpsToRevoke.get(i); 954 appOp.revoke(packageName, context); 955 } 956 957 // TODO: Revoke preferred activities? But this is unnecessary for most roles using it as 958 // they have fallback holders. Moreover, clearing the preferred activity might result in 959 // other system components listening to preferred activity change get notified for the 960 // wrong thing when we are removing a exclusive role holder for adding another. 961 962 if (mBehavior != null) { 963 mBehavior.revoke(this, packageName, context); 964 } 965 966 if (!dontKillApp && permissionOrAppOpChanged) { 967 killApp(packageName, context); 968 } 969 } 970 killApp(@onNull String packageName, @NonNull Context context)971 private void killApp(@NonNull String packageName, @NonNull Context context) { 972 if (DEBUG) { 973 Log.i(LOG_TAG, "Killing " + packageName + " due to " 974 + Thread.currentThread().getStackTrace()[3].getMethodName() 975 + "(" + mName + ")"); 976 } 977 ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, context); 978 if (applicationInfo == null) { 979 Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName); 980 return; 981 } 982 ActivityManager activityManager = context.getSystemService(ActivityManager.class); 983 activityManager.killUid(applicationInfo.uid, "Permission or app op changed"); 984 } 985 986 /** 987 * Check whether the "none" role holder is selected. 988 * 989 * @param context the {@code Context} to retrieve system services 990 * 991 * @return whether the "none" role holder is selected 992 */ isNoneHolderSelected(@onNull Context context)993 private boolean isNoneHolderSelected(@NonNull Context context) { 994 return Utils.getDeviceProtectedSharedPreferences(context).getBoolean( 995 Constants.IS_NONE_ROLE_HOLDER_SELECTED_KEY + mName, false); 996 } 997 998 /** 999 * Callback when a role holder (other than "none") was added. 1000 * 1001 * @param packageName the package name of the role holder 1002 * @param user the user for the role 1003 * @param context the {@code Context} to retrieve system services 1004 */ onHolderAddedAsUser(@onNull String packageName, @NonNull UserHandle user, @NonNull Context context)1005 public void onHolderAddedAsUser(@NonNull String packageName, @NonNull UserHandle user, 1006 @NonNull Context context) { 1007 Utils.getDeviceProtectedSharedPreferences(UserUtils.getUserContext(context, user)).edit() 1008 .remove(Constants.IS_NONE_ROLE_HOLDER_SELECTED_KEY + mName) 1009 .apply(); 1010 } 1011 1012 /** 1013 * Callback when a role holder (other than "none") was selected in the UI and added 1014 * successfully. 1015 * 1016 * @param packageName the package name of the role holder 1017 * @param user the user for the role 1018 * @param context the {@code Context} to retrieve system services 1019 */ onHolderSelectedAsUser(@onNull String packageName, @NonNull UserHandle user, @NonNull Context context)1020 public void onHolderSelectedAsUser(@NonNull String packageName, @NonNull UserHandle user, 1021 @NonNull Context context) { 1022 if (mBehavior != null) { 1023 mBehavior.onHolderSelectedAsUser(this, packageName, user, context); 1024 } 1025 } 1026 1027 /** 1028 * Callback when a role holder changed. 1029 * 1030 * @param user the user for the role 1031 * @param context the {@code Context} to retrieve system services 1032 */ onHolderChangedAsUser(@onNull UserHandle user, @NonNull Context context)1033 public void onHolderChangedAsUser(@NonNull UserHandle user, 1034 @NonNull Context context) { 1035 if (mBehavior != null) { 1036 mBehavior.onHolderChangedAsUser(this, user, context); 1037 } 1038 } 1039 1040 /** 1041 * Callback when the "none" role holder was selected in the UI. 1042 * 1043 * @param user the user for the role 1044 * @param context the {@code Context} to retrieve system services 1045 */ onNoneHolderSelectedAsUser(@onNull UserHandle user, @NonNull Context context)1046 public void onNoneHolderSelectedAsUser(@NonNull UserHandle user, @NonNull Context context) { 1047 Utils.getDeviceProtectedSharedPreferences(UserUtils.getUserContext(context, user)).edit() 1048 .putBoolean(Constants.IS_NONE_ROLE_HOLDER_SELECTED_KEY + mName, true) 1049 .apply(); 1050 } 1051 1052 @Override toString()1053 public String toString() { 1054 return "Role{" 1055 + "mName='" + mName + '\'' 1056 + ", mAllowBypassingQualification=" + mAllowBypassingQualification 1057 + ", mBehavior=" + mBehavior 1058 + ", mDefaultHoldersResourceName=" + mDefaultHoldersResourceName 1059 + ", mDescriptionResource=" + mDescriptionResource 1060 + ", mExclusive=" + mExclusive 1061 + ", mFallBackToDefaultHolder=" + mFallBackToDefaultHolder 1062 + ", mLabelResource=" + mLabelResource 1063 + ", mMinSdkVersion=" + mMinSdkVersion 1064 + ", mOverrideUserWhenGranting=" + mOverrideUserWhenGranting 1065 + ", mRequestDescriptionResource=" + mRequestDescriptionResource 1066 + ", mRequestTitleResource=" + mRequestTitleResource 1067 + ", mRequestable=" + mRequestable 1068 + ", mSearchKeywordsResource=" + mSearchKeywordsResource 1069 + ", mShortLabelResource=" + mShortLabelResource 1070 + ", mShowNone=" + mShowNone 1071 + ", mStatic=" + mStatic 1072 + ", mSystemOnly=" + mSystemOnly 1073 + ", mVisible=" + mVisible 1074 + ", mRequiredComponents=" + mRequiredComponents 1075 + ", mPermissions=" + mPermissions 1076 + ", mAppOpPermissions=" + mAppOpPermissions 1077 + ", mAppOps=" + mAppOps 1078 + ", mPreferredActivities=" + mPreferredActivities 1079 + '}'; 1080 } 1081 } 1082