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