blob: 94e551a11daeb7dbdb3e99891f697d3318877d58 [file] [log] [blame]
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.pm.permission;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
import android.content.pm.parsing.component.ParsedPermission;
import android.os.Build;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import libcore.util.EmptyArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;
/**
* Permission definition.
*/
public final class Permission {
private static final String TAG = "Permission";
public static final int TYPE_MANIFEST = LegacyPermission.TYPE_MANIFEST;
public static final int TYPE_CONFIG = LegacyPermission.TYPE_CONFIG;
public static final int TYPE_DYNAMIC = LegacyPermission.TYPE_DYNAMIC;
@IntDef({
TYPE_MANIFEST,
TYPE_CONFIG,
TYPE_DYNAMIC,
})
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionType {}
@IntDef({
PermissionInfo.PROTECTION_DANGEROUS,
PermissionInfo.PROTECTION_NORMAL,
PermissionInfo.PROTECTION_SIGNATURE,
PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM,
PermissionInfo.PROTECTION_INTERNAL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProtectionLevel {}
@NonNull
private PermissionInfo mPermissionInfo;
private boolean mReconciled;
@PermissionType
private final int mType;
/** UID that owns the definition of this permission */
private int mUid;
/** Additional GIDs given to apps granted this permission */
@NonNull
private int[] mGids = EmptyArray.INT;
/**
* Flag indicating that {@link #mGids} should be adjusted based on the
* {@link UserHandle} the granted app is running as.
*/
private boolean mGidsPerUser;
private boolean mDefinitionChanged;
public Permission(@NonNull String name, @NonNull String packageName,
@PermissionType int type) {
mPermissionInfo = new PermissionInfo();
mPermissionInfo.name = name;
mPermissionInfo.packageName = packageName;
// Default to most conservative protection level.
mPermissionInfo.protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
mType = type;
}
public Permission(@NonNull PermissionInfo permissionInfo, @PermissionType int type) {
mPermissionInfo = permissionInfo;
mType = type;
}
@NonNull
public PermissionInfo getPermissionInfo() {
return mPermissionInfo;
}
public void setPermissionInfo(@Nullable PermissionInfo permissionInfo) {
if (permissionInfo != null) {
mPermissionInfo = permissionInfo;
} else {
final PermissionInfo newPermissionInfo = new PermissionInfo();
newPermissionInfo.name = mPermissionInfo.name;
newPermissionInfo.packageName = mPermissionInfo.packageName;
newPermissionInfo.protectionLevel = mPermissionInfo.protectionLevel;
mPermissionInfo = newPermissionInfo;
}
mReconciled = permissionInfo != null;
}
@NonNull
public String getName() {
return mPermissionInfo.name;
}
public int getProtectionLevel() {
return mPermissionInfo.protectionLevel;
}
@NonNull
public String getPackageName() {
return mPermissionInfo.packageName;
}
public int getType() {
return mType;
}
public int getUid() {
return mUid;
}
public boolean hasGids() {
return mGids.length != 0;
}
@NonNull
public int[] getRawGids() {
return mGids;
}
public boolean areGidsPerUser() {
return mGidsPerUser;
}
public void setGids(@NonNull int[] gids, boolean gidsPerUser) {
mGids = gids;
mGidsPerUser = gidsPerUser;
}
@NonNull
public int[] computeGids(@UserIdInt int userId) {
if (mGidsPerUser) {
final int[] userGids = new int[mGids.length];
for (int i = 0; i < mGids.length; i++) {
final int gid = mGids[i];
userGids[i] = UserHandle.getUid(userId, gid);
}
return userGids;
} else {
return mGids.length != 0 ? mGids.clone() : mGids;
}
}
public boolean isDefinitionChanged() {
return mDefinitionChanged;
}
public void setDefinitionChanged(boolean definitionChanged) {
mDefinitionChanged = definitionChanged;
}
public int calculateFootprint(@NonNull Permission permission) {
if (mUid == permission.mUid) {
return permission.mPermissionInfo.name.length()
+ permission.mPermissionInfo.calculateFootprint();
}
return 0;
}
public boolean isPermission(@NonNull ParsedPermission parsedPermission) {
if (mPermissionInfo == null) {
return false;
}
return Objects.equals(mPermissionInfo.packageName, parsedPermission.getPackageName())
&& Objects.equals(mPermissionInfo.name, parsedPermission.getName());
}
public boolean isDynamic() {
return mType == TYPE_DYNAMIC;
}
public boolean isNormal() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_NORMAL;
}
public boolean isRuntime() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS;
}
public boolean isInstalled() {
return (mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) != 0;
}
public boolean isRemoved() {
return (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0;
}
public boolean isSoftRestricted() {
return (mPermissionInfo.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
}
public boolean isHardRestricted() {
return (mPermissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
}
public boolean isHardOrSoftRestricted() {
return (mPermissionInfo.flags & (PermissionInfo.FLAG_HARD_RESTRICTED
| PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0;
}
public boolean isImmutablyRestricted() {
return (mPermissionInfo.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0;
}
public boolean isSignature() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_SIGNATURE;
}
public boolean isInternal() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_INTERNAL;
}
public boolean isAppOp() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
}
public boolean isDevelopment() {
return isSignature() && (mPermissionInfo.protectionLevel
& PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
}
public boolean isInstaller() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0;
}
public boolean isInstant() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
}
public boolean isOem() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0;
}
public boolean isPre23() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0;
}
public boolean isPreInstalled() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0;
}
public boolean isPrivileged() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
}
public boolean isRuntimeOnly() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
}
public boolean isSetup() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0;
}
public boolean isVerifier() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0;
}
public boolean isVendorPrivileged() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED)
!= 0;
}
public boolean isSystemTextClassifier() {
return (mPermissionInfo.protectionLevel
& PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0;
}
public boolean isDocumenter() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
}
public boolean isConfigurator() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR) != 0;
}
public boolean isIncidentReportApprover() {
return (mPermissionInfo.protectionLevel
& PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0;
}
public boolean isAppPredictor() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR)
!= 0;
}
public boolean isCompanion() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0;
}
public boolean isRetailDemo() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0;
}
public boolean isRecents() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RECENTS) != 0;
}
public boolean isRole() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_ROLE) != 0;
}
public boolean isKnownSigner() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER) != 0;
}
public Set<String> getKnownCerts() {
return mPermissionInfo.knownCerts;
}
public void transfer(@NonNull String oldPackageName, @NonNull String newPackageName) {
if (!oldPackageName.equals(mPermissionInfo.packageName)) {
return;
}
final PermissionInfo newPermissionInfo = new PermissionInfo();
newPermissionInfo.name = mPermissionInfo.name;
newPermissionInfo.packageName = newPackageName;
newPermissionInfo.protectionLevel = mPermissionInfo.protectionLevel;
mPermissionInfo = newPermissionInfo;
mReconciled = false;
mUid = 0;
mGids = EmptyArray.INT;
mGidsPerUser = false;
}
public boolean addToTree(@ProtectionLevel int protectionLevel,
@NonNull PermissionInfo permissionInfo, @NonNull Permission permissionTree) {
final boolean changed =
(mPermissionInfo.protectionLevel != protectionLevel
|| !mReconciled
|| mUid != permissionTree.mUid
|| !Objects.equals(mPermissionInfo.packageName,
permissionTree.mPermissionInfo.packageName)
|| !comparePermissionInfos(mPermissionInfo, permissionInfo));
mPermissionInfo = new PermissionInfo(permissionInfo);
mPermissionInfo.packageName = permissionTree.mPermissionInfo.packageName;
mPermissionInfo.protectionLevel = protectionLevel;
mReconciled = true;
mUid = permissionTree.mUid;
return changed;
}
public void updateDynamicPermission(@NonNull Collection<Permission> permissionTrees) {
if (PackageManagerService.DEBUG_SETTINGS) {
Log.v(TAG, "Dynamic permission: name=" + getName() + " pkg=" + getPackageName()
+ " info=" + mPermissionInfo);
}
if (mType == TYPE_DYNAMIC) {
final Permission tree = findPermissionTree(permissionTrees, mPermissionInfo.name);
if (tree != null) {
mPermissionInfo.packageName = tree.mPermissionInfo.packageName;
mReconciled = true;
mUid = tree.mUid;
}
}
}
public static boolean isOverridingSystemPermission(@Nullable Permission permission,
@NonNull PermissionInfo permissionInfo,
@NonNull PackageManagerInternal packageManagerInternal) {
if (permission == null || Objects.equals(permission.mPermissionInfo.packageName,
permissionInfo.packageName)) {
return false;
}
if (!permission.mReconciled) {
return false;
}
final AndroidPackage currentPackage = packageManagerInternal.getPackage(
permission.mPermissionInfo.packageName);
if (currentPackage == null) {
return false;
}
return currentPackage.isSystem();
}
@NonNull
public static Permission createOrUpdate(@Nullable Permission permission,
@NonNull PermissionInfo permissionInfo, @NonNull AndroidPackage pkg,
@NonNull Collection<Permission> permissionTrees, boolean isOverridingSystemPermission) {
// Allow system apps to redefine non-system permissions
boolean ownerChanged = false;
if (permission != null && !Objects.equals(permission.mPermissionInfo.packageName,
permissionInfo.packageName)) {
if (pkg.isSystem()) {
if (permission.mType == Permission.TYPE_CONFIG && !permission.mReconciled) {
// It's a built-in permission and no owner, take ownership now
permissionInfo.flags |= PermissionInfo.FLAG_INSTALLED;
permission.mPermissionInfo = permissionInfo;
permission.mReconciled = true;
permission.mUid = pkg.getUid();
} else if (!isOverridingSystemPermission) {
Slog.w(TAG, "New decl " + pkg + " of permission "
+ permissionInfo.name + " is system; overriding "
+ permission.mPermissionInfo.packageName);
ownerChanged = true;
permission = null;
}
}
}
boolean wasNonRuntime = permission != null && permission.mType != TYPE_CONFIG
&& !permission.isRuntime();
if (permission == null) {
permission = new Permission(permissionInfo.name, permissionInfo.packageName,
TYPE_MANIFEST);
}
StringBuilder r = null;
if (!permission.mReconciled) {
if (permission.mPermissionInfo.packageName == null
|| permission.mPermissionInfo.packageName.equals(permissionInfo.packageName)) {
final Permission tree = findPermissionTree(permissionTrees, permissionInfo.name);
if (tree == null
|| tree.mPermissionInfo.packageName.equals(permissionInfo.packageName)) {
permissionInfo.flags |= PermissionInfo.FLAG_INSTALLED;
permission.mPermissionInfo = permissionInfo;
permission.mReconciled = true;
permission.mUid = pkg.getUid();
if (PackageManagerService.DEBUG_PACKAGE_SCANNING) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(permissionInfo.name);
}
} else {
Slog.w(TAG, "Permission " + permissionInfo.name + " from package "
+ permissionInfo.packageName + " ignored: base tree "
+ tree.mPermissionInfo.name + " is from package "
+ tree.mPermissionInfo.packageName);
}
} else {
Slog.w(TAG, "Permission " + permissionInfo.name + " from package "
+ permissionInfo.packageName + " ignored: original from "
+ permission.mPermissionInfo.packageName);
}
} else if (PackageManagerService.DEBUG_PACKAGE_SCANNING) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append("DUP:");
r.append(permissionInfo.name);
}
if ((permission.isInternal() && ownerChanged)
|| (permission.isRuntime() && (ownerChanged || wasNonRuntime))) {
// If this is an internal/runtime permission and the owner has changed, or this wasn't a
// runtime permission, then permission state should be cleaned up.
permission.mDefinitionChanged = true;
}
if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
Log.d(TAG, " Permissions: " + r);
}
return permission;
}
@NonNull
public static Permission enforcePermissionTree(@NonNull Collection<Permission> permissionTrees,
@NonNull String permissionName, int callingUid) {
if (permissionName != null) {
final Permission permissionTree = Permission.findPermissionTree(permissionTrees,
permissionName);
if (permissionTree != null) {
if (permissionTree.getUid() == UserHandle.getAppId(callingUid)) {
return permissionTree;
}
throw new SecurityException("Calling uid " + callingUid
+ " is not allowed to add to permission tree "
+ permissionTree.getName() + " owned by uid "
+ permissionTree.getUid());
}
}
throw new SecurityException("No permission tree found for " + permissionName);
}
@Nullable
private static Permission findPermissionTree(@NonNull Collection<Permission> permissionTrees,
@NonNull String permissionName) {
for (final Permission permissionTree : permissionTrees) {
final String permissionTreeName = permissionTree.getName();
if (permissionName.startsWith(permissionTreeName)
&& permissionName.length() > permissionTreeName.length()
&& permissionName.charAt(permissionTreeName.length()) == '.') {
return permissionTree;
}
}
return null;
}
@Nullable
public String getBackgroundPermission() {
return mPermissionInfo.backgroundPermission;
}
@Nullable
public String getGroup() {
return mPermissionInfo.group;
}
public int getProtection() {
return mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
}
public int getProtectionFlags() {
return mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_FLAGS;
}
@NonNull
public PermissionInfo generatePermissionInfo(int flags) {
return generatePermissionInfo(flags, Build.VERSION_CODES.CUR_DEVELOPMENT);
}
@NonNull
public PermissionInfo generatePermissionInfo(int flags, int targetSdkVersion) {
final PermissionInfo permissionInfo;
if (mPermissionInfo != null) {
permissionInfo = new PermissionInfo(mPermissionInfo);
if ((flags & PackageManager.GET_META_DATA) != PackageManager.GET_META_DATA) {
permissionInfo.metaData = null;
}
} else {
permissionInfo = new PermissionInfo();
permissionInfo.name = mPermissionInfo.name;
permissionInfo.packageName = mPermissionInfo.packageName;
permissionInfo.nonLocalizedLabel = mPermissionInfo.name;
}
if (targetSdkVersion >= Build.VERSION_CODES.O) {
permissionInfo.protectionLevel = mPermissionInfo.protectionLevel;
} else {
final int protection = mPermissionInfo.protectionLevel
& PermissionInfo.PROTECTION_MASK_BASE;
if (protection == PermissionInfo.PROTECTION_SIGNATURE) {
// Signature permission's protection flags are always reported.
permissionInfo.protectionLevel = mPermissionInfo.protectionLevel;
} else {
permissionInfo.protectionLevel = protection;
}
}
return permissionInfo;
}
private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
if (pi1.icon != pi2.icon) return false;
if (pi1.logo != pi2.logo) return false;
if (pi1.protectionLevel != pi2.protectionLevel) return false;
if (!Objects.equals(pi1.name, pi2.name)) return false;
if (!Objects.equals(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
// We'll take care of setting this one.
if (!Objects.equals(pi1.packageName, pi2.packageName)) return false;
// These are not currently stored in settings.
//if (!compareStrings(pi1.group, pi2.group)) return false;
//if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
//if (pi1.labelRes != pi2.labelRes) return false;
//if (pi1.descriptionRes != pi2.descriptionRes) return false;
return true;
}
}