Home | History | Annotate | Download | only in model
      1 /*
      2  * Copyright (C) 2015 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.packageinstaller.permission.model;
     18 
     19 import android.app.ActivityManager;
     20 import android.app.AppOpsManager;
     21 import android.content.Context;
     22 import android.content.pm.PackageInfo;
     23 import android.content.pm.PackageItemInfo;
     24 import android.content.pm.PackageManager;
     25 import android.content.pm.PermissionGroupInfo;
     26 import android.content.pm.PermissionInfo;
     27 import android.os.Build;
     28 import android.os.Process;
     29 import android.os.UserHandle;
     30 import android.util.ArrayMap;
     31 
     32 import com.android.packageinstaller.R;
     33 import com.android.packageinstaller.permission.utils.ArrayUtils;
     34 import com.android.packageinstaller.permission.utils.LocationUtils;
     35 
     36 import java.util.ArrayList;
     37 import java.util.List;
     38 
     39 public final class AppPermissionGroup implements Comparable<AppPermissionGroup> {
     40     private static final String PLATFORM_PACKAGE_NAME = "android";
     41 
     42     private static final String KILL_REASON_APP_OP_CHANGE = "Permission related app op changed";
     43 
     44     private final Context mContext;
     45     private final UserHandle mUserHandle;
     46     private final PackageManager mPackageManager;
     47     private final AppOpsManager mAppOps;
     48     private final ActivityManager mActivityManager;
     49 
     50     private final PackageInfo mPackageInfo;
     51     private final String mName;
     52     private final String mDeclaringPackage;
     53     private final CharSequence mLabel;
     54     private final CharSequence mDescription;
     55     private final ArrayMap<String, Permission> mPermissions = new ArrayMap<>();
     56     private final String mIconPkg;
     57     private final int mIconResId;
     58 
     59     private final boolean mAppSupportsRuntimePermissions;
     60     private final boolean mIsEphemeralApp;
     61     private boolean mContainsEphemeralPermission;
     62     private boolean mContainsPreRuntimePermission;
     63 
     64     public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
     65             String permissionName) {
     66         PermissionInfo permissionInfo;
     67         try {
     68             permissionInfo = context.getPackageManager().getPermissionInfo(permissionName, 0);
     69         } catch (PackageManager.NameNotFoundException e) {
     70             return null;
     71         }
     72 
     73         if ((permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
     74                 != PermissionInfo.PROTECTION_DANGEROUS
     75                 || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0
     76                 || (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) {
     77             return null;
     78         }
     79 
     80         PackageItemInfo groupInfo = permissionInfo;
     81         if (permissionInfo.group != null) {
     82             try {
     83                 groupInfo = context.getPackageManager().getPermissionGroupInfo(
     84                         permissionInfo.group, 0);
     85             } catch (PackageManager.NameNotFoundException e) {
     86                 /* ignore */
     87             }
     88         }
     89 
     90         List<PermissionInfo> permissionInfos = null;
     91         if (groupInfo instanceof PermissionGroupInfo) {
     92             try {
     93                 permissionInfos = context.getPackageManager().queryPermissionsByGroup(
     94                         groupInfo.name, 0);
     95             } catch (PackageManager.NameNotFoundException e) {
     96                 /* ignore */
     97             }
     98         }
     99 
    100         return create(context, packageInfo, groupInfo, permissionInfos,
    101                 Process.myUserHandle());
    102     }
    103 
    104     public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
    105             PackageItemInfo groupInfo, List<PermissionInfo> permissionInfos,
    106             UserHandle userHandle) {
    107 
    108         AppPermissionGroup group = new AppPermissionGroup(context, packageInfo, groupInfo.name,
    109                 groupInfo.packageName, groupInfo.loadLabel(context.getPackageManager()),
    110                 loadGroupDescription(context, groupInfo), groupInfo.packageName, groupInfo.icon,
    111                 userHandle);
    112 
    113         if (groupInfo instanceof PermissionInfo) {
    114             permissionInfos = new ArrayList<>();
    115             permissionInfos.add((PermissionInfo) groupInfo);
    116         }
    117 
    118         if (permissionInfos == null || permissionInfos.isEmpty()) {
    119             return null;
    120         }
    121 
    122         final int permissionCount = packageInfo.requestedPermissions.length;
    123         for (int i = 0; i < permissionCount; i++) {
    124             String requestedPermission = packageInfo.requestedPermissions[i];
    125 
    126             PermissionInfo requestedPermissionInfo = null;
    127 
    128             for (PermissionInfo permissionInfo : permissionInfos) {
    129                 if (requestedPermission.equals(permissionInfo.name)) {
    130                     requestedPermissionInfo = permissionInfo;
    131                     break;
    132                 }
    133             }
    134 
    135             if (requestedPermissionInfo == null) {
    136                 continue;
    137             }
    138 
    139             // Collect only runtime permissions.
    140             if ((requestedPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
    141                     != PermissionInfo.PROTECTION_DANGEROUS) {
    142                 continue;
    143             }
    144 
    145             // Don't allow toggling non-platform permission groups for legacy apps via app ops.
    146             if (packageInfo.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1
    147                     && !PLATFORM_PACKAGE_NAME.equals(groupInfo.packageName)) {
    148                 continue;
    149             }
    150 
    151             final boolean granted = (packageInfo.requestedPermissionsFlags[i]
    152                     & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
    153 
    154             final String appOp = PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName)
    155                     ? AppOpsManager.permissionToOp(requestedPermissionInfo.name) : null;
    156 
    157             final boolean appOpAllowed = appOp != null
    158                     && context.getSystemService(AppOpsManager.class).checkOpNoThrow(appOp,
    159                     packageInfo.applicationInfo.uid, packageInfo.packageName)
    160                     == AppOpsManager.MODE_ALLOWED;
    161 
    162             final int flags = context.getPackageManager().getPermissionFlags(
    163                     requestedPermission, packageInfo.packageName, userHandle);
    164 
    165             Permission permission = new Permission(requestedPermission, granted,
    166                     appOp, appOpAllowed, flags, requestedPermissionInfo.protectionLevel);
    167             group.addPermission(permission);
    168         }
    169 
    170         return group;
    171     }
    172 
    173     private static CharSequence loadGroupDescription(Context context, PackageItemInfo group) {
    174         CharSequence description = null;
    175         if (group instanceof PermissionGroupInfo) {
    176             description = ((PermissionGroupInfo) group).loadDescription(
    177                     context.getPackageManager());
    178         } else if (group instanceof PermissionInfo) {
    179             description = ((PermissionInfo) group).loadDescription(
    180                     context.getPackageManager());
    181         }
    182 
    183         if (description == null || description.length() <= 0) {
    184             description = context.getString(R.string.default_permission_description);
    185         }
    186 
    187         return description;
    188     }
    189 
    190     private AppPermissionGroup(Context context, PackageInfo packageInfo, String name,
    191             String declaringPackage, CharSequence label, CharSequence description,
    192             String iconPkg, int iconResId, UserHandle userHandle) {
    193         mContext = context;
    194         mUserHandle = userHandle;
    195         mPackageManager = mContext.getPackageManager();
    196         mPackageInfo = packageInfo;
    197         mAppSupportsRuntimePermissions = packageInfo.applicationInfo
    198                 .targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
    199         mIsEphemeralApp = packageInfo.applicationInfo.isInstantApp();
    200         mAppOps = context.getSystemService(AppOpsManager.class);
    201         mActivityManager = context.getSystemService(ActivityManager.class);
    202         mDeclaringPackage = declaringPackage;
    203         mName = name;
    204         mLabel = label;
    205         mDescription = description;
    206         if (iconResId != 0) {
    207             mIconPkg = iconPkg;
    208             mIconResId = iconResId;
    209         } else {
    210             mIconPkg = context.getPackageName();
    211             mIconResId = R.drawable.ic_perm_device_info;
    212         }
    213     }
    214 
    215     public boolean doesSupportRuntimePermissions() {
    216         return mAppSupportsRuntimePermissions;
    217     }
    218 
    219     public boolean isGrantingAllowed() {
    220         return (!mIsEphemeralApp || mContainsEphemeralPermission)
    221                 && (mAppSupportsRuntimePermissions || mContainsPreRuntimePermission);
    222     }
    223 
    224     public boolean isReviewRequired() {
    225         if (mAppSupportsRuntimePermissions) {
    226             return false;
    227         }
    228         final int permissionCount = mPermissions.size();
    229         for (int i = 0; i < permissionCount; i++) {
    230             Permission permission = mPermissions.valueAt(i);
    231             if (permission.isReviewRequired()) {
    232                 return true;
    233             }
    234         }
    235         return false;
    236     }
    237 
    238     public void resetReviewRequired() {
    239         final int permissionCount = mPermissions.size();
    240         for (int i = 0; i < permissionCount; i++) {
    241             Permission permission = mPermissions.valueAt(i);
    242             if (permission.isReviewRequired()) {
    243                 permission.resetReviewRequired();
    244                 mPackageManager.updatePermissionFlags(permission.getName(),
    245                         mPackageInfo.packageName,
    246                         PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED,
    247                         0, mUserHandle);
    248             }
    249         }
    250     }
    251 
    252     public boolean hasGrantedByDefaultPermission() {
    253         final int permissionCount = mPermissions.size();
    254         for (int i = 0; i < permissionCount; i++) {
    255             Permission permission = mPermissions.valueAt(i);
    256             if (permission.isGrantedByDefault()) {
    257                 return true;
    258             }
    259         }
    260         return false;
    261     }
    262 
    263     public PackageInfo getApp() {
    264         return mPackageInfo;
    265     }
    266 
    267     public String getName() {
    268         return mName;
    269     }
    270 
    271     public String getDeclaringPackage() {
    272         return mDeclaringPackage;
    273     }
    274 
    275     public String getIconPkg() {
    276         return mIconPkg;
    277     }
    278 
    279     public int getIconResId() {
    280         return mIconResId;
    281     }
    282 
    283     public CharSequence getLabel() {
    284         return mLabel;
    285     }
    286 
    287     public CharSequence getDescription() {
    288         return mDescription;
    289     }
    290 
    291     public int getUserId() {
    292         return mUserHandle.getIdentifier();
    293     }
    294 
    295     public boolean hasPermission(String permission) {
    296         return mPermissions.get(permission) != null;
    297     }
    298 
    299     public boolean areRuntimePermissionsGranted() {
    300         return areRuntimePermissionsGranted(null);
    301     }
    302 
    303     public boolean areRuntimePermissionsGranted(String[] filterPermissions) {
    304         if (LocationUtils.isLocationGroupAndProvider(mName, mPackageInfo.packageName)) {
    305             return LocationUtils.isLocationEnabled(mContext);
    306         }
    307         final int permissionCount = mPermissions.size();
    308         for (int i = 0; i < permissionCount; i++) {
    309             Permission permission = mPermissions.valueAt(i);
    310             if (filterPermissions != null
    311                     && !ArrayUtils.contains(filterPermissions, permission.getName())) {
    312                 continue;
    313             }
    314             if (mAppSupportsRuntimePermissions) {
    315                 if (permission.isGranted()) {
    316                     return true;
    317                 }
    318             } else if (permission.isGranted() && (permission.getAppOp() == null
    319                     || permission.isAppOpAllowed()) && !permission.isReviewRequired()) {
    320                 return true;
    321             }
    322         }
    323         return false;
    324     }
    325 
    326     public boolean grantRuntimePermissions(boolean fixedByTheUser) {
    327         return grantRuntimePermissions(fixedByTheUser, null);
    328     }
    329 
    330     public boolean grantRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
    331         final int uid = mPackageInfo.applicationInfo.uid;
    332 
    333         // We toggle permissions only to apps that support runtime
    334         // permissions, otherwise we toggle the app op corresponding
    335         // to the permission if the permission is granted to the app.
    336         for (Permission permission : mPermissions.values()) {
    337             if (filterPermissions != null
    338                     && !ArrayUtils.contains(filterPermissions, permission.getName())) {
    339                 continue;
    340             }
    341 
    342             if (!permission.isGrantingAllowed(mIsEphemeralApp, mAppSupportsRuntimePermissions)) {
    343                 // Skip unallowed permissions.
    344                 continue;
    345             }
    346 
    347             if (mAppSupportsRuntimePermissions) {
    348                 // Do not touch permissions fixed by the system.
    349                 if (permission.isSystemFixed()) {
    350                     return false;
    351                 }
    352 
    353                 // Ensure the permission app op enabled before the permission grant.
    354                 if (permission.hasAppOp() && !permission.isAppOpAllowed()) {
    355                     permission.setAppOpAllowed(true);
    356                     mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_ALLOWED);
    357                 }
    358 
    359                 // Grant the permission if needed.
    360                 if (!permission.isGranted()) {
    361                     permission.setGranted(true);
    362                     mPackageManager.grantRuntimePermission(mPackageInfo.packageName,
    363                             permission.getName(), mUserHandle);
    364                 }
    365 
    366                 // Update the permission flags.
    367                 if (!fixedByTheUser) {
    368                     // Now the apps can ask for the permission as the user
    369                     // no longer has it fixed in a denied state.
    370                     if (permission.isUserFixed() || permission.isUserSet()) {
    371                         permission.setUserFixed(false);
    372                         permission.setUserSet(false);
    373                         mPackageManager.updatePermissionFlags(permission.getName(),
    374                                 mPackageInfo.packageName,
    375                                 PackageManager.FLAG_PERMISSION_USER_FIXED
    376                                         | PackageManager.FLAG_PERMISSION_USER_SET,
    377                                 0, mUserHandle);
    378                     }
    379                 }
    380             } else {
    381                 // Legacy apps cannot have a not granted permission but just in case.
    382                 if (!permission.isGranted()) {
    383                     continue;
    384                 }
    385 
    386                 int killUid = -1;
    387                 int mask = 0;
    388 
    389                 // If the permissions has no corresponding app op, then it is a
    390                 // third-party one and we do not offer toggling of such permissions.
    391                 if (permission.hasAppOp()) {
    392                     if (!permission.isAppOpAllowed()) {
    393                         permission.setAppOpAllowed(true);
    394                         // Enable the app op.
    395                         mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_ALLOWED);
    396 
    397                         // Legacy apps do not know that they have to retry access to a
    398                         // resource due to changes in runtime permissions (app ops in this
    399                         // case). Therefore, we restart them on app op change, so they
    400                         // can pick up the change.
    401                         killUid = uid;
    402                     }
    403 
    404                     // Mark that the permission should not be be granted on upgrade
    405                     // when the app begins supporting runtime permissions.
    406                     if (permission.shouldRevokeOnUpgrade()) {
    407                         permission.setRevokeOnUpgrade(false);
    408                         mask |= PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
    409                     }
    410                 }
    411 
    412                 // Granting a permission explicitly means the user already
    413                 // reviewed it so clear the review flag on every grant.
    414                 if (permission.isReviewRequired()) {
    415                     permission.resetReviewRequired();
    416                     mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
    417                 }
    418 
    419                 if (mask != 0) {
    420                     mPackageManager.updatePermissionFlags(permission.getName(),
    421                             mPackageInfo.packageName, mask, 0, mUserHandle);
    422                 }
    423 
    424                 if (killUid != -1) {
    425                     mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE);
    426                 }
    427             }
    428         }
    429 
    430         return true;
    431     }
    432 
    433     public boolean revokeRuntimePermissions(boolean fixedByTheUser) {
    434         return revokeRuntimePermissions(fixedByTheUser, null);
    435     }
    436 
    437     public boolean revokeRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
    438         final int uid = mPackageInfo.applicationInfo.uid;
    439 
    440         // We toggle permissions only to apps that support runtime
    441         // permissions, otherwise we toggle the app op corresponding
    442         // to the permission if the permission is granted to the app.
    443         for (Permission permission : mPermissions.values()) {
    444             if (filterPermissions != null
    445                     && !ArrayUtils.contains(filterPermissions, permission.getName())) {
    446                 continue;
    447             }
    448 
    449             if (mAppSupportsRuntimePermissions) {
    450                 // Do not touch permissions fixed by the system.
    451                 if (permission.isSystemFixed()) {
    452                     return false;
    453                 }
    454 
    455                 // Revoke the permission if needed.
    456                 if (permission.isGranted()) {
    457                     permission.setGranted(false);
    458                     mPackageManager.revokeRuntimePermission(mPackageInfo.packageName,
    459                             permission.getName(), mUserHandle);
    460                 }
    461 
    462                 // Update the permission flags.
    463                 if (fixedByTheUser) {
    464                     // Take a note that the user fixed the permission.
    465                     if (permission.isUserSet() || !permission.isUserFixed()) {
    466                         permission.setUserSet(false);
    467                         permission.setUserFixed(true);
    468                         mPackageManager.updatePermissionFlags(permission.getName(),
    469                                 mPackageInfo.packageName,
    470                                 PackageManager.FLAG_PERMISSION_USER_SET
    471                                         | PackageManager.FLAG_PERMISSION_USER_FIXED,
    472                                 PackageManager.FLAG_PERMISSION_USER_FIXED,
    473                                 mUserHandle);
    474                     }
    475                 } else {
    476                     if (!permission.isUserSet() || permission.isUserFixed()) {
    477                         permission.setUserSet(true);
    478                         permission.setUserFixed(false);
    479                         // Take a note that the user already chose once.
    480                         mPackageManager.updatePermissionFlags(permission.getName(),
    481                                 mPackageInfo.packageName,
    482                                 PackageManager.FLAG_PERMISSION_USER_SET
    483                                         | PackageManager.FLAG_PERMISSION_USER_FIXED,
    484                                 PackageManager.FLAG_PERMISSION_USER_SET,
    485                                 mUserHandle);
    486                     }
    487                 }
    488             } else {
    489                 // Legacy apps cannot have a non-granted permission but just in case.
    490                 if (!permission.isGranted()) {
    491                     continue;
    492                 }
    493 
    494                 int mask = 0;
    495                 int flags = 0;
    496                 int killUid = -1;
    497 
    498                 // If the permission has no corresponding app op, then it is a
    499                 // third-party one and we do not offer toggling of such permissions.
    500                 if (permission.hasAppOp()) {
    501                     if (permission.isAppOpAllowed()) {
    502                         permission.setAppOpAllowed(false);
    503                         // Disable the app op.
    504                         mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_IGNORED);
    505 
    506                         // Disabling an app op may put the app in a situation in which it
    507                         // has a handle to state it shouldn't have, so we have to kill the
    508                         // app. This matches the revoke runtime permission behavior.
    509                         killUid = uid;
    510                     }
    511 
    512                     // Mark that the permission should not be granted on upgrade
    513                     // when the app begins supporting runtime permissions.
    514                     if (!permission.shouldRevokeOnUpgrade()) {
    515                         permission.setRevokeOnUpgrade(true);
    516                         mask |= PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
    517                         flags |= PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
    518                     }
    519                 }
    520 
    521                 if (mask != 0) {
    522                     mPackageManager.updatePermissionFlags(permission.getName(),
    523                             mPackageInfo.packageName, mask, flags, mUserHandle);
    524                 }
    525 
    526                 if (killUid != -1) {
    527                     mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE);
    528                 }
    529             }
    530         }
    531 
    532         return true;
    533     }
    534 
    535     public void setPolicyFixed() {
    536         final int permissionCount = mPermissions.size();
    537         for (int i = 0; i < permissionCount; i++) {
    538             Permission permission = mPermissions.valueAt(i);
    539             permission.setPolicyFixed(true);
    540             mPackageManager.updatePermissionFlags(permission.getName(),
    541                     mPackageInfo.packageName,
    542                     PackageManager.FLAG_PERMISSION_POLICY_FIXED,
    543                     PackageManager.FLAG_PERMISSION_POLICY_FIXED,
    544                     mUserHandle);
    545         }
    546     }
    547 
    548     public List<Permission> getPermissions() {
    549         return new ArrayList<>(mPermissions.values());
    550     }
    551 
    552     public int getFlags() {
    553         int flags = 0;
    554         final int permissionCount = mPermissions.size();
    555         for (int i = 0; i < permissionCount; i++) {
    556             Permission permission = mPermissions.valueAt(i);
    557             flags |= permission.getFlags();
    558         }
    559         return flags;
    560     }
    561 
    562     public boolean isUserFixed() {
    563         final int permissionCount = mPermissions.size();
    564         for (int i = 0; i < permissionCount; i++) {
    565             Permission permission = mPermissions.valueAt(i);
    566             if (permission.isUserFixed()) {
    567                 return true;
    568             }
    569         }
    570         return false;
    571     }
    572 
    573     public boolean isPolicyFixed() {
    574         final int permissionCount = mPermissions.size();
    575         for (int i = 0; i < permissionCount; i++) {
    576             Permission permission = mPermissions.valueAt(i);
    577             if (permission.isPolicyFixed()) {
    578                 return true;
    579             }
    580         }
    581         return false;
    582     }
    583 
    584     public boolean isUserSet() {
    585         final int permissionCount = mPermissions.size();
    586         for (int i = 0; i < permissionCount; i++) {
    587             Permission permission = mPermissions.valueAt(i);
    588             if (permission.isUserSet()) {
    589                 return true;
    590             }
    591         }
    592         return false;
    593     }
    594 
    595     public boolean isSystemFixed() {
    596         final int permissionCount = mPermissions.size();
    597         for (int i = 0; i < permissionCount; i++) {
    598             Permission permission = mPermissions.valueAt(i);
    599             if (permission.isSystemFixed()) {
    600                 return true;
    601             }
    602         }
    603         return false;
    604     }
    605 
    606     @Override
    607     public int compareTo(AppPermissionGroup another) {
    608         final int result = mLabel.toString().compareTo(another.mLabel.toString());
    609         if (result == 0) {
    610             // Unbadged before badged.
    611             return mPackageInfo.applicationInfo.uid
    612                     - another.mPackageInfo.applicationInfo.uid;
    613         }
    614         return result;
    615     }
    616 
    617     @Override
    618     public boolean equals(Object obj) {
    619         if (this == obj) {
    620             return true;
    621         }
    622 
    623         if (obj == null) {
    624             return false;
    625         }
    626 
    627         if (getClass() != obj.getClass()) {
    628             return false;
    629         }
    630 
    631         AppPermissionGroup other = (AppPermissionGroup) obj;
    632 
    633         if (mName == null) {
    634             if (other.mName != null) {
    635                 return false;
    636             }
    637         } else if (!mName.equals(other.mName)) {
    638             return false;
    639         }
    640 
    641         return true;
    642     }
    643 
    644     @Override
    645     public int hashCode() {
    646         return mName != null ? mName.hashCode() : 0;
    647     }
    648 
    649     @Override
    650     public String toString() {
    651         StringBuilder builder = new StringBuilder();
    652         builder.append(getClass().getSimpleName());
    653         builder.append("{name=").append(mName);
    654         if (!mPermissions.isEmpty()) {
    655             builder.append(", <has permissions>}");
    656         } else {
    657             builder.append('}');
    658         }
    659         return builder.toString();
    660     }
    661 
    662     private void addPermission(Permission permission) {
    663         mPermissions.put(permission.getName(), permission);
    664         if (permission.isEphemeral()) {
    665             mContainsEphemeralPermission = true;
    666         }
    667         if (!permission.isRuntimeOnly()) {
    668             mContainsPreRuntimePermission = true;
    669         }
    670     }
    671 }
    672