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