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