/* * Copyright (C) 2015 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.permissioncontroller.permission.model; import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import androidx.annotation.NonNull; import java.util.ArrayList; import java.util.Objects; /** * A permission and it's properties. * * @see AppPermissionGroup */ public final class Permission { private final @NonNull PermissionInfo mPermissionInfo; private final String mName; private final String mBackgroundPermissionName; private final String mAppOp; private boolean mGranted; private boolean mAppOpAllowed; private int mFlags; private boolean mIsEphemeral; private boolean mIsRuntimeOnly; private Permission mBackgroundPermission; private ArrayList mForegroundPermissions; private boolean mWhitelisted; public Permission(String name, @NonNull PermissionInfo permissionInfo, boolean granted, String appOp, boolean appOpAllowed, int flags) { mPermissionInfo = permissionInfo; mName = name; mBackgroundPermissionName = permissionInfo.backgroundPermission; mGranted = granted; mAppOp = appOp; mAppOpAllowed = appOpAllowed; mFlags = flags; mIsEphemeral = (permissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0; mIsRuntimeOnly = (permissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0; } /** * Mark this permission as background permission for {@code foregroundPermissions}. * * @param foregroundPermission The foreground permission */ public void addForegroundPermissions(Permission foregroundPermission) { if (mForegroundPermissions == null) { mForegroundPermissions = new ArrayList<>(1); } mForegroundPermissions.add(foregroundPermission); } /** * Mark this permission as foreground permission for {@code backgroundPermission}. * * @param backgroundPermission The background permission */ public void setBackgroundPermission(Permission backgroundPermission) { mBackgroundPermission = backgroundPermission; } public PermissionInfo getPermissionInfo() { return mPermissionInfo; } public String getName() { return mName; } public String getAppOp() { return mAppOp; } public int getFlags() { return mFlags; } boolean isHardRestricted() { return (mPermissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0; } boolean isSoftRestricted() { return (mPermissionInfo.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0; } /** * Does this permission affect app ops. * *

I.e. does this permission have a matching app op or is this a background permission. All * background permissions affect the app op of it's assigned foreground permission. * * @return {@code true} if this permission affects app ops */ public boolean affectsAppOp() { return mAppOp != null || isBackgroundPermission(); } /** * Check if the permission is granted. * *

This ignores the state of the app-op. I.e. for apps not handling runtime permissions, this * always returns {@code true}. * * @return If the permission is granted */ public boolean isGranted() { return mGranted; } /** * Check if the permission is granted, also considering the state of the app-op. * *

For the UI, check the grant state of the whole group via * {@link AppPermissionGroup#areRuntimePermissionsGranted}. * * @return {@code true} if the permission (and the app-op) is granted. */ public boolean isGrantedIncludingAppOp() { return mGranted && (!affectsAppOp() || isAppOpAllowed()) && !isReviewRequired(); } public boolean isReviewRequired() { return (mFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0; } public void unsetReviewRequired() { mFlags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; } public void setGranted(boolean mGranted) { this.mGranted = mGranted; } public boolean isAppOpAllowed() { return mAppOpAllowed; } public boolean isUserFixed() { return (mFlags & PackageManager.FLAG_PERMISSION_USER_FIXED) != 0; } public void setUserFixed(boolean userFixed) { if (userFixed) { mFlags |= PackageManager.FLAG_PERMISSION_USER_FIXED; } else { mFlags &= ~PackageManager.FLAG_PERMISSION_USER_FIXED; } } /** * Sets the REVOKE_WHEN_REQUESTED permission flag * @param revokeWhenRequested true to set the flag, false to unset it */ public void setRevokeWhenRequested(boolean revokeWhenRequested) { if (revokeWhenRequested) { mFlags |= PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; } else { mFlags &= ~PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; } } /** * Sets the one-time permission flag * @param oneTime true to set the flag, false to unset it */ public void setOneTime(boolean oneTime) { if (oneTime) { mFlags |= PackageManager.FLAG_PERMISSION_ONE_TIME; } else { mFlags &= ~PackageManager.FLAG_PERMISSION_ONE_TIME; } } public boolean isSelectedLocationAccuracy() { return (mFlags & PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY) != 0; } /** * Sets the selected-location-accuracy permission flag * @param selectedLocationAccuracy true to set the flag, false to unset it */ public void setSelectedLocationAccuracy(boolean selectedLocationAccuracy) { if (selectedLocationAccuracy) { mFlags |= PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY; } else { mFlags &= ~PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY; } } public boolean isSystemFixed() { return (mFlags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0; } public boolean isPolicyFixed() { return (mFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0; } public boolean isUserSet() { return (mFlags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; } public boolean isGrantedByDefault() { return (mFlags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0; } /** * Is the permission user sensitive, i.e. should it always be shown to the user. * *

Non-sensitive permission are usually hidden behind a setting in an overflow menu or * some other kind of flag. * * @return {@code true} if the permission is user sensitive. */ public boolean isUserSensitive() { if (isGrantedIncludingAppOp()) { return (mFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0; } else { return (mFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) != 0; } } /** * If this permission is split into a foreground and background permission, this is the name * of the background permission. * * @return The name of the background permission or {@code null} if the permission is not split */ public String getBackgroundPermissionName() { return mBackgroundPermissionName; } /** * @return If this permission is split into a foreground and background permission, * returns the background permission */ public Permission getBackgroundPermission() { return mBackgroundPermission; } /** * @return If this permission is split into a foreground and background permission, * returns the foreground permission */ public ArrayList getForegroundPermissions() { return mForegroundPermissions; } /** * @return {@code true} iff this is the foreground permission of a background-foreground-split * permission */ public boolean hasBackgroundPermission() { return mBackgroundPermissionName != null; } /** * @return {@code true} iff this is the background permission of a background-foreground-split * permission */ public boolean isBackgroundPermission() { return mForegroundPermissions != null; } /** * @see PackageManager#FLAG_PERMISSION_ONE_TIME */ public boolean isOneTime() { return (mFlags & PackageManager.FLAG_PERMISSION_ONE_TIME) != 0; } public void setUserSet(boolean userSet) { if (userSet) { mFlags |= PackageManager.FLAG_PERMISSION_USER_SET; } else { mFlags &= ~PackageManager.FLAG_PERMISSION_USER_SET; } } public void setPolicyFixed(boolean policyFixed) { if (policyFixed) { mFlags |= PackageManager.FLAG_PERMISSION_POLICY_FIXED; } else { mFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED; } } public boolean isRevokedCompat() { return (mFlags & PackageManager.FLAG_PERMISSION_REVOKED_COMPAT) != 0; } public void setRevokedCompat(boolean revokedCompat) { if (revokedCompat) { mFlags |= PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; } else { mFlags &= ~PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; } } public void setAppOpAllowed(boolean mAppOpAllowed) { this.mAppOpAllowed = mAppOpAllowed; } public boolean isEphemeral() { return mIsEphemeral; } public boolean isRuntimeOnly() { return mIsRuntimeOnly; } public boolean isGrantingAllowed(boolean isEphemeralApp, boolean supportsRuntimePermissions) { return (!isEphemeralApp || isEphemeral()) && (supportsRuntimePermissions || !isRuntimeOnly()); } @Override public boolean equals(Object o) { if (!(o instanceof Permission)) { return false; } Permission other = (Permission) o; if (!Objects.equals(getName(), other.getName()) || getFlags() != other.getFlags() || isGranted() != other.isGranted()) { return false; } // Only compare permission names, in order to avoid recursion if (getBackgroundPermission() != null && other.getBackgroundPermission() != null) { if (!Objects.equals(getBackgroundPermissionName(), other.getBackgroundPermissionName())) { return false; } } else if (getBackgroundPermission() != other.getBackgroundPermission()) { return false; } if (getForegroundPermissions() != null && other.getForegroundPermissions() != null) { ArrayList others = other.getForegroundPermissions(); if (getForegroundPermissions().size() != others.size()) { return false; } for (int i = 0; i < others.size(); i++) { if (!getForegroundPermissions().get(i).getName().equals(others.get(i).getName())) { return false; } } } else if (getForegroundPermissions() != null || other.getForegroundPermissions() != null) { return false; } return Objects.equals(getAppOp(), other.getAppOp()) && isAppOpAllowed() == other.isAppOpAllowed(); } @Override public int hashCode() { ArrayList linkedPermissionNames = new ArrayList<>(); if (mBackgroundPermission != null) { linkedPermissionNames.add(mBackgroundPermission.getName()); } if (mForegroundPermissions != null) { for (Permission linkedPermission: mForegroundPermissions) { if (linkedPermission != null) { linkedPermissionNames.add(linkedPermission.getName()); } } } return Objects.hash(mName, mFlags, mGranted, mAppOp, mAppOpAllowed, linkedPermissionNames); } }