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.permissioncontroller.permission.model; 18 19 import android.content.pm.PackageManager; 20 import android.content.pm.PermissionInfo; 21 22 import androidx.annotation.NonNull; 23 24 import java.util.ArrayList; 25 import java.util.Objects; 26 27 /** 28 * A permission and it's properties. 29 * 30 * @see AppPermissionGroup 31 */ 32 public final class Permission { 33 private final @NonNull PermissionInfo mPermissionInfo; 34 private final String mName; 35 private final String mBackgroundPermissionName; 36 private final String mAppOp; 37 38 private boolean mGranted; 39 private boolean mAppOpAllowed; 40 private int mFlags; 41 private boolean mIsEphemeral; 42 private boolean mIsRuntimeOnly; 43 private Permission mBackgroundPermission; 44 private ArrayList<Permission> mForegroundPermissions; 45 private boolean mWhitelisted; 46 Permission(String name, @NonNull PermissionInfo permissionInfo, boolean granted, String appOp, boolean appOpAllowed, int flags)47 public Permission(String name, @NonNull PermissionInfo permissionInfo, boolean granted, 48 String appOp, boolean appOpAllowed, int flags) { 49 mPermissionInfo = permissionInfo; 50 mName = name; 51 mBackgroundPermissionName = permissionInfo.backgroundPermission; 52 mGranted = granted; 53 mAppOp = appOp; 54 mAppOpAllowed = appOpAllowed; 55 mFlags = flags; 56 mIsEphemeral = 57 (permissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0; 58 mIsRuntimeOnly = 59 (permissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0; 60 } 61 62 /** 63 * Mark this permission as background permission for {@code foregroundPermissions}. 64 * 65 * @param foregroundPermission The foreground permission 66 */ addForegroundPermissions(Permission foregroundPermission)67 public void addForegroundPermissions(Permission foregroundPermission) { 68 if (mForegroundPermissions == null) { 69 mForegroundPermissions = new ArrayList<>(1); 70 } 71 mForegroundPermissions.add(foregroundPermission); 72 } 73 74 /** 75 * Mark this permission as foreground permission for {@code backgroundPermission}. 76 * 77 * @param backgroundPermission The background permission 78 */ setBackgroundPermission(Permission backgroundPermission)79 public void setBackgroundPermission(Permission backgroundPermission) { 80 mBackgroundPermission = backgroundPermission; 81 } 82 getPermissionInfo()83 public PermissionInfo getPermissionInfo() { 84 return mPermissionInfo; 85 } 86 getName()87 public String getName() { 88 return mName; 89 } 90 getAppOp()91 public String getAppOp() { 92 return mAppOp; 93 } 94 getFlags()95 public int getFlags() { 96 return mFlags; 97 } 98 isHardRestricted()99 boolean isHardRestricted() { 100 return (mPermissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0; 101 } 102 isSoftRestricted()103 boolean isSoftRestricted() { 104 return (mPermissionInfo.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0; 105 } 106 107 /** 108 * Does this permission affect app ops. 109 * 110 * <p>I.e. does this permission have a matching app op or is this a background permission. All 111 * background permissions affect the app op of it's assigned foreground permission. 112 * 113 * @return {@code true} if this permission affects app ops 114 */ affectsAppOp()115 public boolean affectsAppOp() { 116 return mAppOp != null || isBackgroundPermission(); 117 } 118 119 /** 120 * Check if the permission is granted. 121 * 122 * <p>This ignores the state of the app-op. I.e. for apps not handling runtime permissions, this 123 * always returns {@code true}. 124 * 125 * @return If the permission is granted 126 */ isGranted()127 public boolean isGranted() { 128 return mGranted; 129 } 130 131 /** 132 * Check if the permission is granted, also considering the state of the app-op. 133 * 134 * <p>For the UI, check the grant state of the whole group via 135 * {@link AppPermissionGroup#areRuntimePermissionsGranted}. 136 * 137 * @return {@code true} if the permission (and the app-op) is granted. 138 */ isGrantedIncludingAppOp()139 public boolean isGrantedIncludingAppOp() { 140 return mGranted && (!affectsAppOp() || isAppOpAllowed()) && !isReviewRequired(); 141 } 142 isReviewRequired()143 public boolean isReviewRequired() { 144 return (mFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0; 145 } 146 unsetReviewRequired()147 public void unsetReviewRequired() { 148 mFlags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; 149 } 150 setGranted(boolean mGranted)151 public void setGranted(boolean mGranted) { 152 this.mGranted = mGranted; 153 } 154 isAppOpAllowed()155 public boolean isAppOpAllowed() { 156 return mAppOpAllowed; 157 } 158 isUserFixed()159 public boolean isUserFixed() { 160 return (mFlags & PackageManager.FLAG_PERMISSION_USER_FIXED) != 0; 161 } 162 setUserFixed(boolean userFixed)163 public void setUserFixed(boolean userFixed) { 164 if (userFixed) { 165 mFlags |= PackageManager.FLAG_PERMISSION_USER_FIXED; 166 } else { 167 mFlags &= ~PackageManager.FLAG_PERMISSION_USER_FIXED; 168 } 169 } 170 171 /** 172 * Sets the one-time permission flag 173 * @param oneTime true to set the flag, false to unset it 174 */ setOneTime(boolean oneTime)175 public void setOneTime(boolean oneTime) { 176 if (oneTime) { 177 mFlags |= PackageManager.FLAG_PERMISSION_ONE_TIME; 178 } else { 179 mFlags &= ~PackageManager.FLAG_PERMISSION_ONE_TIME; 180 } 181 } 182 isSystemFixed()183 public boolean isSystemFixed() { 184 return (mFlags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0; 185 } 186 isPolicyFixed()187 public boolean isPolicyFixed() { 188 return (mFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0; 189 } 190 isUserSet()191 public boolean isUserSet() { 192 return (mFlags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; 193 } 194 isGrantedByDefault()195 public boolean isGrantedByDefault() { 196 return (mFlags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0; 197 } 198 199 /** 200 * Is the permission user sensitive, i.e. should it always be shown to the user. 201 * 202 * <p>Non-sensitive permission are usually hidden behind a setting in an overflow menu or 203 * some other kind of flag. 204 * 205 * @return {@code true} if the permission is user sensitive. 206 */ isUserSensitive()207 public boolean isUserSensitive() { 208 if (isGrantedIncludingAppOp()) { 209 return (mFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0; 210 } else { 211 return (mFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) != 0; 212 } 213 } 214 215 /** 216 * If this permission is split into a foreground and background permission, this is the name 217 * of the background permission. 218 * 219 * @return The name of the background permission or {@code null} if the permission is not split 220 */ getBackgroundPermissionName()221 public String getBackgroundPermissionName() { 222 return mBackgroundPermissionName; 223 } 224 225 /** 226 * @return If this permission is split into a foreground and background permission, 227 * returns the background permission 228 */ getBackgroundPermission()229 public Permission getBackgroundPermission() { 230 return mBackgroundPermission; 231 } 232 233 /** 234 * @return If this permission is split into a foreground and background permission, 235 * returns the foreground permission 236 */ getForegroundPermissions()237 public ArrayList<Permission> getForegroundPermissions() { 238 return mForegroundPermissions; 239 } 240 241 /** 242 * @return {@code true} iff this is the foreground permission of a background-foreground-split 243 * permission 244 */ hasBackgroundPermission()245 public boolean hasBackgroundPermission() { 246 return mBackgroundPermissionName != null; 247 } 248 249 /** 250 * @return {@code true} iff this is the background permission of a background-foreground-split 251 * permission 252 */ isBackgroundPermission()253 public boolean isBackgroundPermission() { 254 return mForegroundPermissions != null; 255 } 256 257 /** 258 * @see PackageManager#FLAG_PERMISSION_ONE_TIME 259 */ isOneTime()260 public boolean isOneTime() { 261 return (mFlags & PackageManager.FLAG_PERMISSION_ONE_TIME) != 0; 262 } 263 setUserSet(boolean userSet)264 public void setUserSet(boolean userSet) { 265 if (userSet) { 266 mFlags |= PackageManager.FLAG_PERMISSION_USER_SET; 267 } else { 268 mFlags &= ~PackageManager.FLAG_PERMISSION_USER_SET; 269 } 270 } 271 setPolicyFixed(boolean policyFixed)272 public void setPolicyFixed(boolean policyFixed) { 273 if (policyFixed) { 274 mFlags |= PackageManager.FLAG_PERMISSION_POLICY_FIXED; 275 } else { 276 mFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED; 277 } 278 } 279 isRevokedCompat()280 public boolean isRevokedCompat() { 281 return (mFlags & PackageManager.FLAG_PERMISSION_REVOKED_COMPAT) != 0; 282 } 283 setRevokedCompat(boolean revokedCompat)284 public void setRevokedCompat(boolean revokedCompat) { 285 if (revokedCompat) { 286 mFlags |= PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; 287 } else { 288 mFlags &= ~PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; 289 } 290 } 291 setAppOpAllowed(boolean mAppOpAllowed)292 public void setAppOpAllowed(boolean mAppOpAllowed) { 293 this.mAppOpAllowed = mAppOpAllowed; 294 } 295 isEphemeral()296 public boolean isEphemeral() { 297 return mIsEphemeral; 298 } 299 isRuntimeOnly()300 public boolean isRuntimeOnly() { 301 return mIsRuntimeOnly; 302 } 303 isGrantingAllowed(boolean isEphemeralApp, boolean supportsRuntimePermissions)304 public boolean isGrantingAllowed(boolean isEphemeralApp, boolean supportsRuntimePermissions) { 305 return (!isEphemeralApp || isEphemeral()) 306 && (supportsRuntimePermissions || !isRuntimeOnly()); 307 } 308 309 @Override equals(Object o)310 public boolean equals(Object o) { 311 if (!(o instanceof Permission)) { 312 return false; 313 } 314 315 Permission other = (Permission) o; 316 317 if (!Objects.equals(getName(), other.getName()) || getFlags() != other.getFlags() 318 || isGranted() != other.isGranted()) { 319 return false; 320 } 321 322 323 // Only compare permission names, in order to avoid recursion 324 if (getBackgroundPermission() != null && other.getBackgroundPermission() != null) { 325 if (!Objects.equals(getBackgroundPermissionName(), 326 other.getBackgroundPermissionName())) { 327 return false; 328 } 329 } else if (getBackgroundPermission() != other.getBackgroundPermission()) { 330 return false; 331 } 332 333 if (getForegroundPermissions() != null && other.getForegroundPermissions() != null) { 334 ArrayList<Permission> others = other.getForegroundPermissions(); 335 if (getForegroundPermissions().size() != others.size()) { 336 return false; 337 } 338 for (int i = 0; i < others.size(); i++) { 339 if (!getForegroundPermissions().get(i).getName().equals(others.get(i).getName())) { 340 return false; 341 } 342 } 343 } else if (getForegroundPermissions() != null || other.getForegroundPermissions() != null) { 344 return false; 345 } 346 347 return Objects.equals(getAppOp(), other.getAppOp()) 348 && isAppOpAllowed() == other.isAppOpAllowed(); 349 } 350 351 @Override hashCode()352 public int hashCode() { 353 ArrayList<String> linkedPermissionNames = new ArrayList<>(); 354 if (mBackgroundPermission != null) { 355 linkedPermissionNames.add(mBackgroundPermission.getName()); 356 } 357 if (mForegroundPermissions != null) { 358 for (Permission linkedPermission: mForegroundPermissions) { 359 if (linkedPermission != null) { 360 linkedPermissionNames.add(linkedPermission.getName()); 361 } 362 } 363 } 364 return Objects.hash(mName, mFlags, mGranted, mAppOp, mAppOpAllowed, linkedPermissionNames); 365 } 366 } 367