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