1 /* 2 * Copyright (C) 2021 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 package com.android.bedstead.testapp; 17 18 import com.android.bedstead.nene.TestApis; 19 import com.android.bedstead.nene.appops.AppOpsMode; 20 import com.android.bedstead.permissions.PermissionContext; 21 import com.android.bedstead.permissions.PermissionsController; 22 import com.android.bedstead.nene.utils.Versions; 23 24 import com.google.errorprone.annotations.CanIgnoreReturnValue; 25 26 import java.util.ArrayList; 27 import java.util.Collections; 28 import java.util.HashSet; 29 import java.util.List; 30 import java.util.Set; 31 32 /** 33 * Permissions management for a test app instance. 34 */ 35 public final class TestAppInstancePermissions implements PermissionsController { 36 37 private final List<TestAppPermissionContext> mPermissionContexts = 38 Collections.synchronizedList(new ArrayList<>()); 39 private final TestAppInstance mTestAppInstance; 40 TestAppInstancePermissions(TestAppInstance testAppInstance)41 TestAppInstancePermissions(TestAppInstance testAppInstance) { 42 mTestAppInstance = testAppInstance; 43 } 44 45 /** 46 * Enter a {@link PermissionContext} where the given permissions are granted to the test app. 47 * 48 * <p>The permission will only be granted for calls made by the test app. 49 * 50 * <p>If the permissions cannot be granted, and are not already granted, an exception will be 51 * thrown. 52 * 53 * <p>Note that only runtime and development permissions can be granted to test apps. 54 * 55 * <p>Recommended usage: 56 * {@code 57 * 58 * try (PermissionContext p = testApp.permissions().withPermission(PERMISSION1, PERMISSION2) { 59 * // Code which needs the permissions goes here 60 * } 61 * } 62 */ 63 @Override 64 @CanIgnoreReturnValue withPermission(String... permissions)65 public TestAppPermissionContext withPermission(String... permissions) { 66 TestAppPermissionContext context = 67 new TestAppPermissionContext(this); 68 mPermissionContexts.add(context); 69 context.withPermission(permissions); 70 71 return context; 72 } 73 74 /** 75 * Enter a {@link PermissionContext} where the given permissions are granted to the test app. 76 * 77 * <p>The permission will only be granted for calls made by the test app. 78 * 79 * <p>If the permissions cannot be granted, and are not already granted, an exception will be 80 * thrown. 81 * 82 * <p>Note that only runtime and development permissions can be granted to test apps. 83 * 84 * <p>The permission will only be granted on the given version. 85 * 86 * <p>Recommended usage: 87 * {@code 88 * 89 * try (PermissionContext p = testApp.permissions() 90 * .withPermissionOnVersion(R, PERMISSION1, PERMISSION2) { 91 * // Code which needs the permissions goes here 92 * } 93 * } 94 */ 95 @Override 96 @CanIgnoreReturnValue withPermissionOnVersion(int sdkVersion, String... permissions)97 public TestAppPermissionContext withPermissionOnVersion(int sdkVersion, String... permissions) { 98 return withPermissionOnVersionBetween(sdkVersion, sdkVersion, permissions); 99 } 100 101 /** 102 * Enter a {@link PermissionContext} where the given permissions are granted to the test app. 103 * 104 * <p>The permission will only be granted for calls made by the test app. 105 * 106 * <p>If the permissions cannot be granted, and are not already granted, an exception will be 107 * thrown. 108 * 109 * <p>Note that only runtime and development permissions can be granted to test apps. 110 * 111 * <p>The permission will only be granted on the given version or higher. 112 * 113 * <p>Recommended usage: 114 * {@code 115 * 116 * try (PermissionContext p = testApp.permissions() 117 * .withPermissionOnVersionAtLest(R, PERMISSION1, PERMISSION2) { 118 * // Code which needs the permissions goes here 119 * } 120 * } 121 */ 122 @Override 123 @CanIgnoreReturnValue withPermissionOnVersionAtLeast( int minSdkVersion, String... permissions)124 public TestAppPermissionContext withPermissionOnVersionAtLeast( 125 int minSdkVersion, String... permissions) { 126 return withPermissionOnVersionBetween(minSdkVersion, Versions.ANY, permissions); 127 } 128 129 /** 130 * Enter a {@link PermissionContext} where the given permissions are granted to the test app. 131 * 132 * <p>The permission will only be granted for calls made by the test app. 133 * 134 * <p>If the permissions cannot be granted, and are not already granted, an exception will be 135 * thrown. 136 * 137 * <p>Note that only runtime and development permissions can be granted to test apps. 138 * 139 * <p>The permission will only be granted on the given version or lower 140 * 141 * <p>Recommended usage: 142 * {@code 143 * 144 * try (PermissionContext p = testApp.permissions() 145 * .withPermissionOnVersionAtMost(R, PERMISSION1, PERMISSION2) { 146 * // Code which needs the permissions goes here 147 * } 148 * } 149 */ 150 @Override 151 @CanIgnoreReturnValue withPermissionOnVersionAtMost( int maxSdkVersion, String... permissions)152 public TestAppPermissionContext withPermissionOnVersionAtMost( 153 int maxSdkVersion, String... permissions) { 154 return withPermissionOnVersionBetween(Versions.ANY, maxSdkVersion, permissions); 155 } 156 157 /** 158 * Enter a {@link PermissionContext} where the given permissions are granted to the test app. 159 * 160 * <p>The permission will only be granted for calls made by the test app. 161 * 162 * <p>If the permissions cannot be granted, and are not already granted, an exception will be 163 * thrown. 164 * 165 * <p>Note that only runtime and development permissions can be granted to test apps. 166 * 167 * <p>The permission will only be granted on versions between those given (inclusive). 168 * 169 * <p>Recommended usage: 170 * {@code 171 * 172 * try (PermissionContext p = testApp.permissions() 173 * .withPermissionOnVersionBetween(R, T, PERMISSION1, PERMISSION2) { 174 * // Code which needs the permissions goes here 175 * } 176 * } 177 */ 178 @Override 179 @CanIgnoreReturnValue withPermissionOnVersionBetween( int minSdkVersion, int maxSdkVersion, String... permissions)180 public TestAppPermissionContext withPermissionOnVersionBetween( 181 int minSdkVersion, int maxSdkVersion, String... permissions) { 182 TestAppPermissionContext context = 183 new TestAppPermissionContext(this); 184 mPermissionContexts.add(context); 185 context.withPermissionOnVersionBetween(minSdkVersion, maxSdkVersion, permissions); 186 187 return context; 188 } 189 190 /** 191 * Enter a {@link PermissionContext} where the given permissions are not granted to the test 192 * app. 193 * 194 * <p>The permission will only guarantee to not be granted for calls made by the test app. 195 * 196 * <p>If the permissions cannot be denied an exception will be thrown. 197 * 198 * <p>Recommended usage: 199 * {@code 200 * 201 * try (PermissionContext p = testApp.permissions() 202 * .withoutPermission(PERMISSION1, PERMISSION2) { 203 * // Code which needs the permissions goes here 204 * } 205 * } 206 */ 207 @Override 208 @CanIgnoreReturnValue withoutPermission(String... permissions)209 public TestAppPermissionContext withoutPermission(String... permissions) { 210 TestAppPermissionContext context = 211 new TestAppPermissionContext(this); 212 mPermissionContexts.add(context); 213 context.withoutPermission(permissions); 214 215 return context; 216 } 217 218 /** 219 * Enter a {@link PermissionContext} where the given app op are granted to the test 220 * app. 221 * 222 * <p>The app op will only guarantee to be granted for calls made by the test app. 223 * 224 * <p>If the app op cannot be granted an exception will be thrown. 225 * 226 * <p>Recommended usage: 227 * {@code 228 * 229 * try (PermissionContext p = testApp.permissions() 230 * .withAppOp(APP_OP1, APP_OP2) { 231 * // Code which needs the app op goes here 232 * } 233 * } 234 */ 235 @Override 236 @CanIgnoreReturnValue withAppOp(String... appOps)237 public TestAppPermissionContext withAppOp(String... appOps) { 238 TestAppPermissionContext context = 239 new TestAppPermissionContext(this); 240 mPermissionContexts.add(context); 241 context.withAppOp(appOps); 242 243 return context; 244 } 245 246 /** 247 * Enter a {@link PermissionContext} where the given app op are granted to the test 248 * app. 249 * 250 * <p>The app op will only guarantee to be granted for calls made by the test app. 251 * 252 * <p>If the app op cannot be granted an exception will be thrown. 253 * 254 * <p>The app op will only be granted on the version given 255 * 256 * <p>Recommended usage: 257 * {@code 258 * 259 * try (PermissionContext p = testApp.permissions() 260 * .withAppOpOnVersion(R, APP_OP1, APP_OP2) { 261 * // Code which needs the app op goes here 262 * } 263 * } 264 */ 265 @Override 266 @CanIgnoreReturnValue withAppOpOnVersion(int sdkVersion, String... appOps)267 public TestAppPermissionContext withAppOpOnVersion(int sdkVersion, String... appOps) { 268 return withAppOpOnVersionBetween(sdkVersion, sdkVersion, appOps); 269 } 270 271 /** 272 * Enter a {@link PermissionContext} where the given app op are granted to the test 273 * app. 274 * 275 * <p>The app op will only guarantee to be granted for calls made by the test app. 276 * 277 * <p>If the app op cannot be granted an exception will be thrown. 278 * 279 * <p>The app op will only be granted on the version given and above 280 * 281 * <p>Recommended usage: 282 * {@code 283 * 284 * try (PermissionContext p = testApp.permissions() 285 * .withAppOpOnVersionAtLeast(R, APP_OP1, APP_OP2) { 286 * // Code which needs the app op goes here 287 * } 288 * } 289 */ 290 @Override 291 @CanIgnoreReturnValue withAppOpOnVersionAtLeast(int minSdkVersion, String... appOps)292 public TestAppPermissionContext withAppOpOnVersionAtLeast(int minSdkVersion, String... appOps) { 293 return withAppOpOnVersionBetween(minSdkVersion, Versions.ANY, appOps); 294 } 295 296 /** 297 * Enter a {@link PermissionContext} where the given app op are granted to the test 298 * app. 299 * 300 * <p>The app op will only guarantee to be granted for calls made by the test app. 301 * 302 * <p>If the app op cannot be granted an exception will be thrown. 303 * 304 * <p>The app op will only be granted on the version given and below 305 * 306 * <p>Recommended usage: 307 * {@code 308 * 309 * try (PermissionContext p = testApp.permissions() 310 * .withAppOpOnVersionAtMost(S, APP_OP1, APP_OP2) { 311 * // Code which needs the app op goes here 312 * } 313 * } 314 */ 315 @Override 316 @CanIgnoreReturnValue withAppOpOnVersionAtMost(int maxSdkVersion, String... appOps)317 public TestAppPermissionContext withAppOpOnVersionAtMost(int maxSdkVersion, String... appOps) { 318 return withAppOpOnVersionBetween(Versions.ANY, maxSdkVersion, appOps); 319 } 320 321 /** 322 * Enter a {@link PermissionContext} where the given app op are granted to the test 323 * app. 324 * 325 * <p>The app op will only guarantee to be granted for calls made by the test app. 326 * 327 * <p>If the app op cannot be granted an exception will be thrown. 328 * 329 * <p>The app op will only be granted on versions between the min and max (inclusive) 330 * 331 * <p>Recommended usage: 332 * {@code 333 * 334 * try (PermissionContext p = testApp.permissions() 335 * .withAppOpOnVersionBetween(R, S, APP_OP1, APP_OP2) { 336 * // Code which needs the app op goes here 337 * } 338 * } 339 */ 340 @Override 341 @CanIgnoreReturnValue withAppOpOnVersionBetween( int minSdkVersion, int maxSdkVersion, String... appOps)342 public TestAppPermissionContext withAppOpOnVersionBetween( 343 int minSdkVersion, int maxSdkVersion, String... appOps) { 344 TestAppPermissionContext context = 345 new TestAppPermissionContext(this); 346 mPermissionContexts.add(context); 347 context.withAppOpOnVersionBetween(minSdkVersion, maxSdkVersion, appOps); 348 349 return context; 350 } 351 352 /** 353 * Enter a {@link PermissionContext} where the given app op are not granted to the test 354 * app. 355 * 356 * <p>The app op will only guarantee to not be granted for calls made by the test app. 357 * 358 * <p>If the app op cannot be denied an exception will be thrown. 359 * 360 * <p>Recommended usage: 361 * {@code 362 * 363 * try (PermissionContext p = testApp.permissions() 364 * .withoutAppOp(APP_OP1, APP_OP2) { 365 * // Code which needs to not have the app op goes here 366 * } 367 * } 368 */ 369 @Override 370 @CanIgnoreReturnValue withoutAppOp(String... appOps)371 public TestAppPermissionContext withoutAppOp(String... appOps) { 372 TestAppPermissionContext context = new TestAppPermissionContext(this); 373 mPermissionContexts.add(context); 374 context.withoutAppOp(appOps); 375 376 return context; 377 } 378 applyPermissions()379 void applyPermissions() { 380 Set<String> grantedPermissions = new HashSet<>(); 381 Set<String> deniedPermissions = new HashSet<>(); 382 Set<String> grantedAppOps = new HashSet<>(); 383 Set<String> deniedAppOps = new HashSet<>(); 384 synchronized (mPermissionContexts) { 385 for (TestAppPermissionContext permissionContext : mPermissionContexts) { 386 for (String permission : permissionContext.grantedPermissions()) { 387 grantedPermissions.add(permission); 388 deniedPermissions.remove(permission); 389 } 390 391 for (String permission : permissionContext.deniedPermissions()) { 392 grantedPermissions.remove(permission); 393 deniedPermissions.add(permission); 394 } 395 396 for (String appOp : permissionContext.grantedAppOps()) { 397 grantedAppOps.add(appOp); 398 deniedAppOps.remove(appOp); 399 } 400 401 for (String appOp : permissionContext.deniedAppOps()) { 402 grantedAppOps.remove(appOp); 403 deniedAppOps.add(appOp); 404 } 405 } 406 } 407 408 TestApis.permissions().setPermissionState( 409 mTestAppInstance.testApp().pkg(), 410 mTestAppInstance.user(), 411 grantedPermissions, 412 deniedPermissions); 413 TestApis.permissions().setAppOpState(mTestAppInstance.testApp().pkg(), 414 mTestAppInstance.user(), 415 grantedAppOps, 416 deniedAppOps); 417 } 418 undoPermission(TestAppPermissionContext permissionContext)419 void undoPermission(TestAppPermissionContext permissionContext) { 420 mPermissionContexts.remove(permissionContext); 421 applyPermissions(); 422 } 423 clearPermissions()424 void clearPermissions() { 425 mPermissionContexts.clear(); 426 applyPermissions(); 427 } 428 } 429