1 /* 2 * Copyright (C) 2006 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.server.pm.permission; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.UserIdInt; 23 import android.content.pm.PackageManager; 24 import android.content.pm.PackageManagerInternal; 25 import android.content.pm.PermissionInfo; 26 import android.os.Build; 27 import android.os.UserHandle; 28 import android.util.Log; 29 import android.util.Slog; 30 31 import com.android.server.pm.PackageManagerService; 32 import com.android.server.pm.pkg.PackageState; 33 import com.android.server.pm.pkg.component.ParsedPermission; 34 35 import libcore.util.EmptyArray; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.util.Collection; 40 import java.util.Objects; 41 import java.util.Set; 42 43 /** 44 * Permission definition. 45 */ 46 public final class Permission { 47 private static final String TAG = "Permission"; 48 49 public static final int TYPE_MANIFEST = LegacyPermission.TYPE_MANIFEST; 50 public static final int TYPE_CONFIG = LegacyPermission.TYPE_CONFIG; 51 public static final int TYPE_DYNAMIC = LegacyPermission.TYPE_DYNAMIC; 52 @IntDef({ 53 TYPE_MANIFEST, 54 TYPE_CONFIG, 55 TYPE_DYNAMIC, 56 }) 57 @Retention(RetentionPolicy.SOURCE) 58 public @interface PermissionType {} 59 60 @IntDef({ 61 PermissionInfo.PROTECTION_DANGEROUS, 62 PermissionInfo.PROTECTION_NORMAL, 63 PermissionInfo.PROTECTION_SIGNATURE, 64 PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM, 65 PermissionInfo.PROTECTION_INTERNAL, 66 }) 67 @Retention(RetentionPolicy.SOURCE) 68 public @interface ProtectionLevel {} 69 70 @NonNull 71 private PermissionInfo mPermissionInfo; 72 73 private boolean mReconciled; 74 75 @PermissionType 76 private final int mType; 77 78 /** UID that owns the definition of this permission */ 79 private int mUid; 80 81 /** Additional GIDs given to apps granted this permission */ 82 @NonNull 83 private int[] mGids = EmptyArray.INT; 84 85 /** 86 * Flag indicating that {@link #mGids} should be adjusted based on the 87 * {@link UserHandle} the granted app is running as. 88 */ 89 private boolean mGidsPerUser; 90 91 private boolean mDefinitionChanged; 92 Permission(@onNull String name, @NonNull String packageName, @PermissionType int type)93 public Permission(@NonNull String name, @NonNull String packageName, 94 @PermissionType int type) { 95 mPermissionInfo = new PermissionInfo(); 96 mPermissionInfo.name = name; 97 mPermissionInfo.packageName = packageName; 98 // Default to most conservative protection level. 99 mPermissionInfo.protectionLevel = PermissionInfo.PROTECTION_SIGNATURE; 100 mType = type; 101 } 102 Permission(@onNull PermissionInfo permissionInfo, @PermissionType int type)103 public Permission(@NonNull PermissionInfo permissionInfo, @PermissionType int type) { 104 mPermissionInfo = permissionInfo; 105 mType = type; 106 } 107 Permission(@onNull PermissionInfo permissionInfo, @PermissionType int type, boolean reconciled, int uid, int[] gids, boolean gidsPerUser)108 public Permission(@NonNull PermissionInfo permissionInfo, @PermissionType int type, 109 boolean reconciled, int uid, int[] gids, boolean gidsPerUser) { 110 this(permissionInfo, type); 111 mReconciled = reconciled; 112 mUid = uid; 113 mGids = gids; 114 mGidsPerUser = gidsPerUser; 115 } 116 117 @NonNull getPermissionInfo()118 public PermissionInfo getPermissionInfo() { 119 return mPermissionInfo; 120 } 121 setPermissionInfo(@ullable PermissionInfo permissionInfo)122 public void setPermissionInfo(@Nullable PermissionInfo permissionInfo) { 123 if (permissionInfo != null) { 124 mPermissionInfo = permissionInfo; 125 } else { 126 final PermissionInfo newPermissionInfo = new PermissionInfo(); 127 newPermissionInfo.name = mPermissionInfo.name; 128 newPermissionInfo.packageName = mPermissionInfo.packageName; 129 newPermissionInfo.protectionLevel = mPermissionInfo.protectionLevel; 130 mPermissionInfo = newPermissionInfo; 131 } 132 mReconciled = permissionInfo != null; 133 } 134 135 @NonNull getName()136 public String getName() { 137 return mPermissionInfo.name; 138 } 139 getProtectionLevel()140 public int getProtectionLevel() { 141 return mPermissionInfo.protectionLevel; 142 } 143 144 @NonNull getPackageName()145 public String getPackageName() { 146 return mPermissionInfo.packageName; 147 } 148 getType()149 public int getType() { 150 return mType; 151 } 152 getUid()153 public int getUid() { 154 return mUid; 155 } 156 hasGids()157 public boolean hasGids() { 158 return mGids.length != 0; 159 } 160 161 @NonNull getRawGids()162 public int[] getRawGids() { 163 return mGids; 164 } 165 areGidsPerUser()166 public boolean areGidsPerUser() { 167 return mGidsPerUser; 168 } 169 setGids(@onNull int[] gids, boolean gidsPerUser)170 public void setGids(@NonNull int[] gids, boolean gidsPerUser) { 171 mGids = gids; 172 mGidsPerUser = gidsPerUser; 173 } 174 175 @NonNull computeGids(@serIdInt int userId)176 public int[] computeGids(@UserIdInt int userId) { 177 if (mGidsPerUser) { 178 final int[] userGids = new int[mGids.length]; 179 for (int i = 0; i < mGids.length; i++) { 180 final int gid = mGids[i]; 181 userGids[i] = UserHandle.getUid(userId, gid); 182 } 183 return userGids; 184 } else { 185 return mGids.length != 0 ? mGids.clone() : mGids; 186 } 187 } 188 isDefinitionChanged()189 public boolean isDefinitionChanged() { 190 return mDefinitionChanged; 191 } 192 setDefinitionChanged(boolean definitionChanged)193 public void setDefinitionChanged(boolean definitionChanged) { 194 mDefinitionChanged = definitionChanged; 195 } 196 calculateFootprint(@onNull Permission permission)197 public int calculateFootprint(@NonNull Permission permission) { 198 if (mUid == permission.mUid) { 199 return permission.mPermissionInfo.name.length() 200 + permission.mPermissionInfo.calculateFootprint(); 201 } 202 return 0; 203 } 204 isPermission(@onNull ParsedPermission parsedPermission)205 public boolean isPermission(@NonNull ParsedPermission parsedPermission) { 206 if (mPermissionInfo == null) { 207 return false; 208 } 209 return Objects.equals(mPermissionInfo.packageName, parsedPermission.getPackageName()) 210 && Objects.equals(mPermissionInfo.name, parsedPermission.getName()); 211 } 212 isDynamic()213 public boolean isDynamic() { 214 return mType == TYPE_DYNAMIC; 215 } 216 isNormal()217 public boolean isNormal() { 218 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) 219 == PermissionInfo.PROTECTION_NORMAL; 220 } isRuntime()221 public boolean isRuntime() { 222 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) 223 == PermissionInfo.PROTECTION_DANGEROUS; 224 } 225 isRemoved()226 public boolean isRemoved() { 227 return (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0; 228 } 229 isSoftRestricted()230 public boolean isSoftRestricted() { 231 return (mPermissionInfo.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0; 232 } 233 isHardRestricted()234 public boolean isHardRestricted() { 235 return (mPermissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0; 236 } 237 isHardOrSoftRestricted()238 public boolean isHardOrSoftRestricted() { 239 return (mPermissionInfo.flags & (PermissionInfo.FLAG_HARD_RESTRICTED 240 | PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0; 241 } 242 isImmutablyRestricted()243 public boolean isImmutablyRestricted() { 244 return (mPermissionInfo.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0; 245 } 246 isSignature()247 public boolean isSignature() { 248 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) 249 == PermissionInfo.PROTECTION_SIGNATURE; 250 } 251 isInternal()252 public boolean isInternal() { 253 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) 254 == PermissionInfo.PROTECTION_INTERNAL; 255 } 256 isAppOp()257 public boolean isAppOp() { 258 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0; 259 } 260 isDevelopment()261 public boolean isDevelopment() { 262 return isSignature() && (mPermissionInfo.protectionLevel 263 & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0; 264 } 265 isInstaller()266 public boolean isInstaller() { 267 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0; 268 } 269 isInstant()270 public boolean isInstant() { 271 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0; 272 } 273 isOem()274 public boolean isOem() { 275 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0; 276 } 277 isPre23()278 public boolean isPre23() { 279 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0; 280 } 281 isPreInstalled()282 public boolean isPreInstalled() { 283 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0; 284 } 285 isPrivileged()286 public boolean isPrivileged() { 287 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0; 288 } 289 isRuntimeOnly()290 public boolean isRuntimeOnly() { 291 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0; 292 } 293 isSetup()294 public boolean isSetup() { 295 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0; 296 } 297 isVerifier()298 public boolean isVerifier() { 299 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0; 300 } 301 isVendorPrivileged()302 public boolean isVendorPrivileged() { 303 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) 304 != 0; 305 } 306 isSystemTextClassifier()307 public boolean isSystemTextClassifier() { 308 return (mPermissionInfo.protectionLevel 309 & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0; 310 } 311 isConfigurator()312 public boolean isConfigurator() { 313 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR) != 0; 314 } 315 isIncidentReportApprover()316 public boolean isIncidentReportApprover() { 317 return (mPermissionInfo.protectionLevel 318 & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0; 319 } 320 isAppPredictor()321 public boolean isAppPredictor() { 322 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) 323 != 0; 324 } 325 isCompanion()326 public boolean isCompanion() { 327 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0; 328 } 329 isModule()330 public boolean isModule() { 331 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_MODULE) != 0; 332 } 333 isRetailDemo()334 public boolean isRetailDemo() { 335 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0; 336 } 337 isRecents()338 public boolean isRecents() { 339 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RECENTS) != 0; 340 } 341 isRole()342 public boolean isRole() { 343 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_ROLE) != 0; 344 } 345 isKnownSigner()346 public boolean isKnownSigner() { 347 return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER) != 0; 348 } 349 getKnownCerts()350 public Set<String> getKnownCerts() { 351 return mPermissionInfo.knownCerts; 352 } 353 transfer(@onNull String oldPackageName, @NonNull String newPackageName)354 public void transfer(@NonNull String oldPackageName, @NonNull String newPackageName) { 355 if (!oldPackageName.equals(mPermissionInfo.packageName)) { 356 return; 357 } 358 final PermissionInfo newPermissionInfo = new PermissionInfo(); 359 newPermissionInfo.name = mPermissionInfo.name; 360 newPermissionInfo.packageName = newPackageName; 361 newPermissionInfo.protectionLevel = mPermissionInfo.protectionLevel; 362 mPermissionInfo = newPermissionInfo; 363 mReconciled = false; 364 mUid = 0; 365 mGids = EmptyArray.INT; 366 mGidsPerUser = false; 367 } 368 addToTree(@rotectionLevel int protectionLevel, @NonNull PermissionInfo permissionInfo, @NonNull Permission permissionTree)369 public boolean addToTree(@ProtectionLevel int protectionLevel, 370 @NonNull PermissionInfo permissionInfo, @NonNull Permission permissionTree) { 371 final boolean changed = 372 (mPermissionInfo.protectionLevel != protectionLevel 373 || !mReconciled 374 || mUid != permissionTree.mUid 375 || !Objects.equals(mPermissionInfo.packageName, 376 permissionTree.mPermissionInfo.packageName) 377 || !comparePermissionInfos(mPermissionInfo, permissionInfo)); 378 mPermissionInfo = new PermissionInfo(permissionInfo); 379 mPermissionInfo.packageName = permissionTree.mPermissionInfo.packageName; 380 mPermissionInfo.protectionLevel = protectionLevel; 381 mReconciled = true; 382 mUid = permissionTree.mUid; 383 return changed; 384 } 385 updateDynamicPermission(@onNull Collection<Permission> permissionTrees)386 public void updateDynamicPermission(@NonNull Collection<Permission> permissionTrees) { 387 if (PackageManagerService.DEBUG_SETTINGS) { 388 Log.v(TAG, "Dynamic permission: name=" + getName() + " pkg=" + getPackageName() 389 + " info=" + mPermissionInfo); 390 } 391 if (mType == TYPE_DYNAMIC) { 392 final Permission tree = findPermissionTree(permissionTrees, mPermissionInfo.name); 393 if (tree != null) { 394 mPermissionInfo.packageName = tree.mPermissionInfo.packageName; 395 mReconciled = true; 396 mUid = tree.mUid; 397 } 398 } 399 } 400 isOverridingSystemPermission(@ullable Permission permission, @NonNull PermissionInfo permissionInfo, @NonNull PackageManagerInternal packageManagerInternal)401 public static boolean isOverridingSystemPermission(@Nullable Permission permission, 402 @NonNull PermissionInfo permissionInfo, 403 @NonNull PackageManagerInternal packageManagerInternal) { 404 if (permission == null || Objects.equals(permission.mPermissionInfo.packageName, 405 permissionInfo.packageName)) { 406 return false; 407 } 408 if (!permission.mReconciled) { 409 return false; 410 } 411 var currentPackageState = packageManagerInternal.getPackageStateInternal( 412 permission.mPermissionInfo.packageName); 413 if (currentPackageState == null) { 414 return false; 415 } 416 return currentPackageState.isSystem(); 417 } 418 419 @NonNull createOrUpdate(@ullable Permission permission, @NonNull PermissionInfo permissionInfo, @NonNull PackageState packageState, @NonNull Collection<Permission> permissionTrees, boolean isOverridingSystemPermission)420 public static Permission createOrUpdate(@Nullable Permission permission, 421 @NonNull PermissionInfo permissionInfo, @NonNull PackageState packageState, 422 @NonNull Collection<Permission> permissionTrees, boolean isOverridingSystemPermission) { 423 // Allow system apps to redefine non-system permissions 424 boolean ownerChanged = false; 425 if (permission != null && !Objects.equals(permission.mPermissionInfo.packageName, 426 permissionInfo.packageName)) { 427 if (packageState.isSystem()) { 428 if (permission.mType == Permission.TYPE_CONFIG && !permission.mReconciled) { 429 // It's a built-in permission and no owner, take ownership now 430 permission.mPermissionInfo = permissionInfo; 431 permission.mReconciled = true; 432 permission.mUid = packageState.getAppId(); 433 } else if (!isOverridingSystemPermission) { 434 Slog.w(TAG, "New decl " + packageState + " of permission " 435 + permissionInfo.name + " is system; overriding " 436 + permission.mPermissionInfo.packageName); 437 ownerChanged = true; 438 permission = null; 439 } 440 } 441 } 442 boolean wasNonInternal = permission != null && permission.mType != TYPE_CONFIG 443 && !permission.isInternal(); 444 boolean wasNonRuntime = permission != null && permission.mType != TYPE_CONFIG 445 && !permission.isRuntime(); 446 if (permission == null) { 447 permission = new Permission(permissionInfo.name, permissionInfo.packageName, 448 TYPE_MANIFEST); 449 } 450 StringBuilder r = null; 451 if (!permission.mReconciled) { 452 if (permission.mPermissionInfo.packageName == null 453 || permission.mPermissionInfo.packageName.equals(permissionInfo.packageName)) { 454 final Permission tree = findPermissionTree(permissionTrees, permissionInfo.name); 455 if (tree == null 456 || tree.mPermissionInfo.packageName.equals(permissionInfo.packageName)) { 457 permission.mPermissionInfo = permissionInfo; 458 permission.mReconciled = true; 459 permission.mUid = packageState.getAppId(); 460 if (PackageManagerService.DEBUG_PACKAGE_SCANNING) { 461 if (r == null) { 462 r = new StringBuilder(256); 463 } else { 464 r.append(' '); 465 } 466 r.append(permissionInfo.name); 467 } 468 } else { 469 Slog.w(TAG, "Permission " + permissionInfo.name + " from package " 470 + permissionInfo.packageName + " ignored: base tree " 471 + tree.mPermissionInfo.name + " is from package " 472 + tree.mPermissionInfo.packageName); 473 } 474 } else { 475 Slog.w(TAG, "Permission " + permissionInfo.name + " from package " 476 + permissionInfo.packageName + " ignored: original from " 477 + permission.mPermissionInfo.packageName); 478 } 479 } else if (PackageManagerService.DEBUG_PACKAGE_SCANNING) { 480 if (r == null) { 481 r = new StringBuilder(256); 482 } else { 483 r.append(' '); 484 } 485 r.append("DUP:"); 486 r.append(permissionInfo.name); 487 } 488 if ((permission.isInternal() && (ownerChanged || wasNonInternal)) 489 || (permission.isRuntime() && (ownerChanged || wasNonRuntime))) { 490 // If this is an internal/runtime permission and the owner has changed, or this wasn't a 491 // internal/runtime permission, then permission state should be cleaned up. 492 permission.mDefinitionChanged = true; 493 } 494 if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) { 495 Log.d(TAG, " Permissions: " + r); 496 } 497 return permission; 498 } 499 500 @NonNull enforcePermissionTree(@onNull Collection<Permission> permissionTrees, @NonNull String permissionName, int callingUid)501 public static Permission enforcePermissionTree(@NonNull Collection<Permission> permissionTrees, 502 @NonNull String permissionName, int callingUid) { 503 if (permissionName != null) { 504 final Permission permissionTree = Permission.findPermissionTree(permissionTrees, 505 permissionName); 506 if (permissionTree != null) { 507 if (permissionTree.getUid() == UserHandle.getAppId(callingUid)) { 508 return permissionTree; 509 } 510 } 511 } 512 throw new SecurityException("Calling uid " + callingUid 513 + " is not allowed to add to or remove from the permission tree"); 514 } 515 516 @Nullable findPermissionTree(@onNull Collection<Permission> permissionTrees, @NonNull String permissionName)517 private static Permission findPermissionTree(@NonNull Collection<Permission> permissionTrees, 518 @NonNull String permissionName) { 519 for (final Permission permissionTree : permissionTrees) { 520 final String permissionTreeName = permissionTree.getName(); 521 if (permissionName.startsWith(permissionTreeName) 522 && permissionName.length() > permissionTreeName.length() 523 && permissionName.charAt(permissionTreeName.length()) == '.') { 524 return permissionTree; 525 } 526 } 527 return null; 528 } 529 530 @Nullable getBackgroundPermission()531 public String getBackgroundPermission() { 532 return mPermissionInfo.backgroundPermission; 533 } 534 535 @Nullable getGroup()536 public String getGroup() { 537 return mPermissionInfo.group; 538 } 539 getProtection()540 public int getProtection() { 541 return mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; 542 } 543 getProtectionFlags()544 public int getProtectionFlags() { 545 return mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_FLAGS; 546 } 547 548 @NonNull generatePermissionInfo(int flags)549 public PermissionInfo generatePermissionInfo(int flags) { 550 return generatePermissionInfo(flags, Build.VERSION_CODES.CUR_DEVELOPMENT); 551 } 552 553 @NonNull generatePermissionInfo(int flags, int targetSdkVersion)554 public PermissionInfo generatePermissionInfo(int flags, int targetSdkVersion) { 555 final PermissionInfo permissionInfo; 556 if (mPermissionInfo != null) { 557 permissionInfo = new PermissionInfo(mPermissionInfo); 558 if ((flags & PackageManager.GET_META_DATA) != PackageManager.GET_META_DATA) { 559 permissionInfo.metaData = null; 560 } 561 } else { 562 permissionInfo = new PermissionInfo(); 563 permissionInfo.name = mPermissionInfo.name; 564 permissionInfo.packageName = mPermissionInfo.packageName; 565 permissionInfo.nonLocalizedLabel = mPermissionInfo.name; 566 } 567 // A Permission in PermissionRegistry is always installed. 568 permissionInfo.flags |= PermissionInfo.FLAG_INSTALLED; 569 if (targetSdkVersion >= Build.VERSION_CODES.O) { 570 permissionInfo.protectionLevel = mPermissionInfo.protectionLevel; 571 } else { 572 final int protection = mPermissionInfo.protectionLevel 573 & PermissionInfo.PROTECTION_MASK_BASE; 574 if (protection == PermissionInfo.PROTECTION_SIGNATURE) { 575 // Signature permission's protection flags are always reported. 576 permissionInfo.protectionLevel = mPermissionInfo.protectionLevel; 577 } else { 578 permissionInfo.protectionLevel = protection; 579 } 580 } 581 return permissionInfo; 582 } 583 comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2)584 private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) { 585 if (pi1.icon != pi2.icon) return false; 586 if (pi1.logo != pi2.logo) return false; 587 if (pi1.protectionLevel != pi2.protectionLevel) return false; 588 if (!Objects.equals(pi1.name, pi2.name)) return false; 589 if (!Objects.equals(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false; 590 // We'll take care of setting this one. 591 if (!Objects.equals(pi1.packageName, pi2.packageName)) return false; 592 // These are not currently stored in settings. 593 //if (!compareStrings(pi1.group, pi2.group)) return false; 594 //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false; 595 //if (pi1.labelRes != pi2.labelRes) return false; 596 //if (pi1.descriptionRes != pi2.descriptionRes) return false; 597 return true; 598 } 599 } 600