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