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 REVOKE_WHEN_REQUESTED permission flag 173 * @param revokeWhenRequested true to set the flag, false to unset it 174 */ setRevokeWhenRequested(boolean revokeWhenRequested)175 public void setRevokeWhenRequested(boolean revokeWhenRequested) { 176 if (revokeWhenRequested) { 177 mFlags |= PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; 178 } else { 179 mFlags &= ~PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; 180 } 181 } 182 183 /** 184 * Sets the one-time permission flag 185 * @param oneTime true to set the flag, false to unset it 186 */ setOneTime(boolean oneTime)187 public void setOneTime(boolean oneTime) { 188 if (oneTime) { 189 mFlags |= PackageManager.FLAG_PERMISSION_ONE_TIME; 190 } else { 191 mFlags &= ~PackageManager.FLAG_PERMISSION_ONE_TIME; 192 } 193 } 194 isSelectedLocationAccuracy()195 public boolean isSelectedLocationAccuracy() { 196 return (mFlags & PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY) != 0; 197 } 198 199 /** 200 * Sets the selected-location-accuracy permission flag 201 * @param selectedLocationAccuracy true to set the flag, false to unset it 202 */ setSelectedLocationAccuracy(boolean selectedLocationAccuracy)203 public void setSelectedLocationAccuracy(boolean selectedLocationAccuracy) { 204 if (selectedLocationAccuracy) { 205 mFlags |= PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY; 206 } else { 207 mFlags &= ~PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY; 208 } 209 } 210 isSystemFixed()211 public boolean isSystemFixed() { 212 return (mFlags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0; 213 } 214 isPolicyFixed()215 public boolean isPolicyFixed() { 216 return (mFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0; 217 } 218 isUserSet()219 public boolean isUserSet() { 220 return (mFlags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; 221 } 222 isGrantedByDefault()223 public boolean isGrantedByDefault() { 224 return (mFlags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0; 225 } 226 227 /** 228 * Is the permission user sensitive, i.e. should it always be shown to the user. 229 * 230 * <p>Non-sensitive permission are usually hidden behind a setting in an overflow menu or 231 * some other kind of flag. 232 * 233 * @return {@code true} if the permission is user sensitive. 234 */ isUserSensitive()235 public boolean isUserSensitive() { 236 if (isGrantedIncludingAppOp()) { 237 return (mFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0; 238 } else { 239 return (mFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) != 0; 240 } 241 } 242 243 /** 244 * If this permission is split into a foreground and background permission, this is the name 245 * of the background permission. 246 * 247 * @return The name of the background permission or {@code null} if the permission is not split 248 */ getBackgroundPermissionName()249 public String getBackgroundPermissionName() { 250 return mBackgroundPermissionName; 251 } 252 253 /** 254 * @return If this permission is split into a foreground and background permission, 255 * returns the background permission 256 */ getBackgroundPermission()257 public Permission getBackgroundPermission() { 258 return mBackgroundPermission; 259 } 260 261 /** 262 * @return If this permission is split into a foreground and background permission, 263 * returns the foreground permission 264 */ getForegroundPermissions()265 public ArrayList<Permission> getForegroundPermissions() { 266 return mForegroundPermissions; 267 } 268 269 /** 270 * @return {@code true} iff this is the foreground permission of a background-foreground-split 271 * permission 272 */ hasBackgroundPermission()273 public boolean hasBackgroundPermission() { 274 return mBackgroundPermissionName != null; 275 } 276 277 /** 278 * @return {@code true} iff this is the background permission of a background-foreground-split 279 * permission 280 */ isBackgroundPermission()281 public boolean isBackgroundPermission() { 282 return mForegroundPermissions != null; 283 } 284 285 /** 286 * @see PackageManager#FLAG_PERMISSION_ONE_TIME 287 */ isOneTime()288 public boolean isOneTime() { 289 return (mFlags & PackageManager.FLAG_PERMISSION_ONE_TIME) != 0; 290 } 291 setUserSet(boolean userSet)292 public void setUserSet(boolean userSet) { 293 if (userSet) { 294 mFlags |= PackageManager.FLAG_PERMISSION_USER_SET; 295 } else { 296 mFlags &= ~PackageManager.FLAG_PERMISSION_USER_SET; 297 } 298 } 299 setPolicyFixed(boolean policyFixed)300 public void setPolicyFixed(boolean policyFixed) { 301 if (policyFixed) { 302 mFlags |= PackageManager.FLAG_PERMISSION_POLICY_FIXED; 303 } else { 304 mFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED; 305 } 306 } 307 isRevokedCompat()308 public boolean isRevokedCompat() { 309 return (mFlags & PackageManager.FLAG_PERMISSION_REVOKED_COMPAT) != 0; 310 } 311 setRevokedCompat(boolean revokedCompat)312 public void setRevokedCompat(boolean revokedCompat) { 313 if (revokedCompat) { 314 mFlags |= PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; 315 } else { 316 mFlags &= ~PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; 317 } 318 } 319 setAppOpAllowed(boolean mAppOpAllowed)320 public void setAppOpAllowed(boolean mAppOpAllowed) { 321 this.mAppOpAllowed = mAppOpAllowed; 322 } 323 isEphemeral()324 public boolean isEphemeral() { 325 return mIsEphemeral; 326 } 327 isRuntimeOnly()328 public boolean isRuntimeOnly() { 329 return mIsRuntimeOnly; 330 } 331 isGrantingAllowed(boolean isEphemeralApp, boolean supportsRuntimePermissions)332 public boolean isGrantingAllowed(boolean isEphemeralApp, boolean supportsRuntimePermissions) { 333 return (!isEphemeralApp || isEphemeral()) 334 && (supportsRuntimePermissions || !isRuntimeOnly()); 335 } 336 337 @Override equals(Object o)338 public boolean equals(Object o) { 339 if (!(o instanceof Permission)) { 340 return false; 341 } 342 343 Permission other = (Permission) o; 344 345 if (!Objects.equals(getName(), other.getName()) || getFlags() != other.getFlags() 346 || isGranted() != other.isGranted()) { 347 return false; 348 } 349 350 351 // Only compare permission names, in order to avoid recursion 352 if (getBackgroundPermission() != null && other.getBackgroundPermission() != null) { 353 if (!Objects.equals(getBackgroundPermissionName(), 354 other.getBackgroundPermissionName())) { 355 return false; 356 } 357 } else if (getBackgroundPermission() != other.getBackgroundPermission()) { 358 return false; 359 } 360 361 if (getForegroundPermissions() != null && other.getForegroundPermissions() != null) { 362 ArrayList<Permission> others = other.getForegroundPermissions(); 363 if (getForegroundPermissions().size() != others.size()) { 364 return false; 365 } 366 for (int i = 0; i < others.size(); i++) { 367 if (!getForegroundPermissions().get(i).getName().equals(others.get(i).getName())) { 368 return false; 369 } 370 } 371 } else if (getForegroundPermissions() != null || other.getForegroundPermissions() != null) { 372 return false; 373 } 374 375 return Objects.equals(getAppOp(), other.getAppOp()) 376 && isAppOpAllowed() == other.isAppOpAllowed(); 377 } 378 379 @Override hashCode()380 public int hashCode() { 381 ArrayList<String> linkedPermissionNames = new ArrayList<>(); 382 if (mBackgroundPermission != null) { 383 linkedPermissionNames.add(mBackgroundPermission.getName()); 384 } 385 if (mForegroundPermissions != null) { 386 for (Permission linkedPermission: mForegroundPermissions) { 387 if (linkedPermission != null) { 388 linkedPermissionNames.add(linkedPermission.getName()); 389 } 390 } 391 } 392 return Objects.hash(mName, mFlags, mGranted, mAppOp, mAppOpAllowed, linkedPermissionNames); 393 } 394 } 395