1 /* 2 * Copyright (C) 2023 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 android.virtualdevice.cts.common; 18 19 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS; 20 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; 21 22 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 23 24 import static com.google.common.truth.Truth.assertThat; 25 26 import static org.junit.Assume.assumeFalse; 27 import static org.junit.Assume.assumeNotNull; 28 import static org.junit.Assume.assumeTrue; 29 30 import android.Manifest; 31 import android.app.Activity; 32 import android.app.ActivityOptions; 33 import android.companion.AssociationInfo; 34 import android.companion.virtual.VirtualDeviceManager; 35 import android.companion.virtual.VirtualDeviceManager.VirtualDevice; 36 import android.companion.virtual.VirtualDeviceParams; 37 import android.content.ComponentName; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.hardware.display.DisplayManager; 41 import android.hardware.display.VirtualDisplay; 42 import android.hardware.display.VirtualDisplayConfig; 43 import android.media.ImageReader; 44 import android.os.Build; 45 import android.os.Bundle; 46 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 47 import android.server.wm.Condition; 48 import android.server.wm.WindowManagerState; 49 import android.server.wm.WindowManagerStateHelper; 50 import android.view.Display; 51 import android.view.Surface; 52 53 import androidx.annotation.NonNull; 54 import androidx.annotation.Nullable; 55 import androidx.core.os.BuildCompat; 56 import androidx.test.filters.SdkSuppress; 57 58 import com.android.compatibility.common.util.AdoptShellPermissionsRule; 59 import com.android.compatibility.common.util.FeatureUtil; 60 61 import org.junit.rules.ExternalResource; 62 import org.junit.rules.RuleChain; 63 import org.junit.rules.TestRule; 64 import org.junit.runner.Description; 65 import org.junit.runners.model.Statement; 66 67 import java.util.ArrayList; 68 import java.util.Arrays; 69 import java.util.function.BooleanSupplier; 70 import java.util.function.Supplier; 71 import java.util.stream.Stream; 72 73 /** 74 * A test rule that allows for testing VDM and virtual device features. 75 * 76 * <p>The interaction with VDM / CDM requires the test to execute a lot of statements with specific 77 * permissions held by Shell. Running logic in the individual tests with the Shell permission 78 * identity may interfere with the rule setup and teardown.</p> 79 * 80 * <p>Do not use this rule alongside {@link AdoptShellPermissionsRule} or any other method to adopt 81 * the Shell permission identity, like 82 * {@link com.android.compatibility.common.util.SystemUtil#runWithShellPermissionIdentity}. Use 83 * the utilities provided here (e.g. {@link VirtualDeviceRule#runWithTemporaryPermission}, which 84 * will re-acquire the necessary permissions after the execution of the statement.</p> 85 * 86 * <p>Similarly, {@link android.platform.test.flag.junit.CheckFlagsRule} may interfere with the 87 * permissions depending on the rule order because it also adopts the Shell identity and drops it 88 * during teardown. Do not use this rule alongside the {@link VirtualDeviceRule}, it already 89 * incorporates the flag rule in the correct order and the flag values can be accessed in the tests. 90 * </p> 91 */ 92 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = "VanillaIceCream") 93 public class VirtualDeviceRule implements TestRule { 94 95 /** General permissions needed for creating virtual devices and displays. */ 96 private static final String[] REQUIRED_PERMISSIONS = 97 new String[] { 98 Manifest.permission.CREATE_VIRTUAL_DEVICE, 99 Manifest.permission.ADD_TRUSTED_DISPLAY, 100 Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, 101 Manifest.permission.ADD_MIRROR_DISPLAY, 102 Manifest.permission.ASSOCIATE_COMPANION_DEVICES, 103 Manifest.permission.MANAGE_ROLE_HOLDERS, 104 Manifest.permission.BYPASS_ROLE_QUALIFICATION, 105 "android.permission.MANAGE_COMPANION_DEVICES", 106 }; 107 108 public static final VirtualDeviceParams DEFAULT_VIRTUAL_DEVICE_PARAMS = 109 new VirtualDeviceParams.Builder().build(); 110 111 public static final String DEFAULT_VIRTUAL_DISPLAY_NAME = "testVirtualDisplay"; 112 public static final int DEFAULT_VIRTUAL_DISPLAY_WIDTH = 640; 113 public static final int DEFAULT_VIRTUAL_DISPLAY_HEIGHT = 480; 114 public static final int DEFAULT_VIRTUAL_DISPLAY_DPI = 240; 115 116 public static final ComponentName BLOCKED_ACTIVITY_COMPONENT = 117 new ComponentName("android", "com.android.internal.app.BlockedAppStreamingActivity"); 118 119 private final String[] mPermissions; 120 private final FakeAssociationRule mFakeAssociationRule = new FakeAssociationRule(); 121 private final VirtualDeviceTrackerRule mTrackerRule = new VirtualDeviceTrackerRule(); 122 123 private final Context mContext = getInstrumentation().getTargetContext(); 124 private final VirtualDeviceManager mVirtualDeviceManager = 125 mContext.getSystemService(VirtualDeviceManager.class); 126 private final WindowManagerStateHelper mWmState = new WindowManagerStateHelper(); 127 128 /** A default virtual device for tests that only use the rule to access VDM functionality. */ 129 private VirtualDevice mDefaultVirtualDevice = null; 130 131 /** Creates a rule with the required permissions for creating virtual devices and displays. */ createDefault()132 public static VirtualDeviceRule createDefault() { 133 return new VirtualDeviceRule(); 134 } 135 136 /** Creates a rule with any additional permission needed for the specific test. */ withAdditionalPermissions(String... additionalPermissions)137 public static VirtualDeviceRule withAdditionalPermissions(String... additionalPermissions) { 138 return new VirtualDeviceRule(additionalPermissions); 139 } 140 VirtualDeviceRule(String... permissions)141 private VirtualDeviceRule(String... permissions) { 142 mPermissions = 143 Stream.concat(Arrays.stream(REQUIRED_PERMISSIONS), Arrays.stream(permissions)) 144 .toArray(String[]::new); 145 } 146 147 @Override apply(final Statement base, final Description description)148 public Statement apply(final Statement base, final Description description) { 149 assumeNotNull(mVirtualDeviceManager); 150 return RuleChain 151 .outerRule(DeviceFlagsValueProvider.createCheckFlagsRule()) 152 .around(new AdoptShellPermissionsRule( 153 getInstrumentation().getUiAutomation(), mPermissions)) 154 .around(mFakeAssociationRule) 155 .around(mTrackerRule) 156 .apply(base, description); 157 } 158 159 /** 160 * Returns a default virtual device. 161 */ getDefaultVirtualDevice()162 public VirtualDevice getDefaultVirtualDevice() { 163 if (mDefaultVirtualDevice == null) { 164 mDefaultVirtualDevice = createManagedVirtualDevice(); 165 } 166 return mDefaultVirtualDevice; 167 } 168 169 /** 170 * Returns the VirtualDevice object for the given deviceId 171 */ getVirtualDevice(int deviceId)172 public android.companion.virtual.VirtualDevice getVirtualDevice(int deviceId) { 173 if (BuildCompat.isAtLeastV()) { 174 return mVirtualDeviceManager.getVirtualDevice(deviceId); 175 } else { 176 return mVirtualDeviceManager.getVirtualDevices().stream() 177 .filter(device -> device.getDeviceId() == deviceId).findFirst().orElse(null); 178 } 179 } 180 181 /** 182 * Creates a virtual device with default params that will be automatically closed when the 183 * test is torn down. 184 */ 185 @NonNull createManagedVirtualDevice()186 public VirtualDevice createManagedVirtualDevice() { 187 return createManagedVirtualDevice(DEFAULT_VIRTUAL_DEVICE_PARAMS); 188 } 189 190 /** 191 * Creates a virtual device with the given params that will be automatically closed when the 192 * test is torn down. 193 */ 194 @NonNull createManagedVirtualDevice(@onNull VirtualDeviceParams params)195 public VirtualDevice createManagedVirtualDevice(@NonNull VirtualDeviceParams params) { 196 final VirtualDevice virtualDevice = mVirtualDeviceManager.createVirtualDevice( 197 mFakeAssociationRule.getAssociationInfo().getId(), params); 198 mTrackerRule.mVirtualDevices.add(virtualDevice); 199 return virtualDevice; 200 } 201 202 /** 203 * Creates a virtual display associated with the given device that will be automatically 204 * released when the test is torn down. 205 */ 206 @Nullable createManagedVirtualDisplay(@onNull VirtualDevice virtualDevice)207 public VirtualDisplay createManagedVirtualDisplay(@NonNull VirtualDevice virtualDevice) { 208 return createManagedVirtualDisplay(virtualDevice, 209 createDefaultVirtualDisplayConfigBuilder()); 210 } 211 212 /** 213 * Creates a virtual display associated with the given device and flags that will be 214 * automatically released when the test is torn down. 215 */ 216 @Nullable createManagedVirtualDisplayWithFlags( @onNull VirtualDevice virtualDevice, int flags)217 public VirtualDisplay createManagedVirtualDisplayWithFlags( 218 @NonNull VirtualDevice virtualDevice, int flags) { 219 return createManagedVirtualDisplay(virtualDevice, 220 createDefaultVirtualDisplayConfigBuilder().setFlags(flags)); 221 } 222 223 /** 224 * Creates a virtual display associated with the given device and config that will be 225 * automatically released when the test is torn down. 226 */ 227 @Nullable createManagedVirtualDisplay(@onNull VirtualDevice virtualDevice, @NonNull VirtualDisplayConfig.Builder builder)228 public VirtualDisplay createManagedVirtualDisplay(@NonNull VirtualDevice virtualDevice, 229 @NonNull VirtualDisplayConfig.Builder builder) { 230 return createManagedVirtualDisplay(virtualDevice, builder, /* callback= */ null); 231 } 232 233 /** 234 * Creates a virtual display associated with the given device and config that will be 235 * automatically released when the test is torn down. 236 */ 237 @Nullable createManagedVirtualDisplay(@onNull VirtualDevice virtualDevice, @NonNull VirtualDisplayConfig.Builder builder, @Nullable VirtualDisplay.Callback callback)238 public VirtualDisplay createManagedVirtualDisplay(@NonNull VirtualDevice virtualDevice, 239 @NonNull VirtualDisplayConfig.Builder builder, 240 @Nullable VirtualDisplay.Callback callback) { 241 VirtualDisplayConfig config = builder.build(); 242 final Surface surface = prepareSurface(config.getWidth(), config.getHeight()); 243 final VirtualDisplay virtualDisplay = virtualDevice.createVirtualDisplay( 244 builder.setSurface(surface).build(), Runnable::run, callback); 245 if (virtualDisplay != null) { 246 assertDisplayExists(virtualDisplay.getDisplay().getDisplayId()); 247 // There's no need to track managed virtual displays to have them released on tear-down 248 // because they will be released automatically when the VirtualDevice is closed. 249 } 250 return virtualDisplay; 251 } 252 253 /** 254 * Creates a virtual display not associated with the any virtual device that will be 255 * automatically released when the test is torn down. 256 */ 257 @Nullable createManagedUnownedVirtualDisplay()258 public VirtualDisplay createManagedUnownedVirtualDisplay() { 259 return createManagedUnownedVirtualDisplay(createTrustedVirtualDisplayConfigBuilder()); 260 } 261 262 /** 263 * Creates a virtual display not associated with the any virtual device with the given flags 264 * that will be automatically released when the test is torn down. 265 */ 266 @Nullable createManagedUnownedVirtualDisplayWithFlags(int flags)267 public VirtualDisplay createManagedUnownedVirtualDisplayWithFlags(int flags) { 268 return createManagedUnownedVirtualDisplay( 269 createDefaultVirtualDisplayConfigBuilder().setFlags(flags)); 270 } 271 272 /** 273 * Creates a virtual display not associated with the any virtual device with the given config 274 * that will be automatically released when the test is torn down. 275 */ 276 @Nullable createManagedUnownedVirtualDisplay( @onNull VirtualDisplayConfig.Builder builder)277 public VirtualDisplay createManagedUnownedVirtualDisplay( 278 @NonNull VirtualDisplayConfig.Builder builder) { 279 VirtualDisplayConfig config = builder.build(); 280 final Surface surface = prepareSurface(config.getWidth(), config.getHeight()); 281 final VirtualDisplay virtualDisplay = 282 mContext.getSystemService(DisplayManager.class).createVirtualDisplay( 283 builder.setSurface(surface).build()); 284 if (virtualDisplay != null) { 285 assertDisplayExists(virtualDisplay.getDisplay().getDisplayId()); 286 mTrackerRule.mVirtualDisplays.add(virtualDisplay); 287 } 288 return virtualDisplay; 289 } 290 291 /** 292 * Default config for virtual display creation, with a predefined name, dimensions and an empty 293 * surface. 294 */ 295 @NonNull createDefaultVirtualDisplayConfigBuilder()296 public static VirtualDisplayConfig.Builder createDefaultVirtualDisplayConfigBuilder() { 297 return createDefaultVirtualDisplayConfigBuilder( 298 DEFAULT_VIRTUAL_DISPLAY_WIDTH, DEFAULT_VIRTUAL_DISPLAY_HEIGHT); 299 } 300 301 /** 302 * Config for trusted virtual display creation, which applies the flags 303 * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED}, 304 * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC} and 305 * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}. A predefined name and 306 * dimensions and an empty surface are used. 307 */ 308 @NonNull createTrustedVirtualDisplayConfigBuilder()309 public static VirtualDisplayConfig.Builder createTrustedVirtualDisplayConfigBuilder() { 310 return createDefaultVirtualDisplayConfigBuilder().setFlags( 311 DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC 312 | DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED 313 | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY); 314 } 315 316 /** 317 * Default config for virtual display creation with custom dimensions. 318 */ 319 @NonNull createDefaultVirtualDisplayConfigBuilder( int width, int height)320 public static VirtualDisplayConfig.Builder createDefaultVirtualDisplayConfigBuilder( 321 int width, int height) { 322 return new VirtualDisplayConfig.Builder( 323 DEFAULT_VIRTUAL_DISPLAY_NAME, width, height, DEFAULT_VIRTUAL_DISPLAY_DPI); 324 } 325 326 /** 327 * Blocks until the display with the given ID is available. 328 */ assertDisplayExists(int displayId)329 public void assertDisplayExists(int displayId) { 330 waitAndAssertWindowManagerState("Waiting for display to be available", 331 () -> mWmState.getDisplay(displayId) != null); 332 } 333 334 /** 335 * Blocks until the display with the given ID is removed. 336 */ assertDisplayDoesNotExist(int displayId)337 public void assertDisplayDoesNotExist(int displayId) { 338 waitAndAssertWindowManagerState("Waiting for display to be removed", 339 () -> mWmState.getDisplay(displayId) == null); 340 } 341 342 /** Returns the WM state helper. */ getWmState()343 public WindowManagerStateHelper getWmState() { 344 return mWmState; 345 } 346 347 /** Creates a new CDM association. */ createManagedAssociation(String deviceProfile)348 public AssociationInfo createManagedAssociation(String deviceProfile) { 349 return mFakeAssociationRule.createManagedAssociation(deviceProfile); 350 } 351 352 /** Drops the current CDM association. */ dropCompanionDeviceAssociation()353 public void dropCompanionDeviceAssociation() { 354 mFakeAssociationRule.disassociate(); 355 } 356 357 /** Acquires all permissions needed for the VDM test setup and teardown. */ acquireNecessaryPermissions()358 public void acquireNecessaryPermissions() { 359 getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(mPermissions); 360 } 361 362 /** 363 * Temporarily assumes the given permissions and executes the given supplier. Reverts any 364 * permissions currently held after the execution. 365 */ runWithTemporaryPermission(Supplier<T> supplier, String... permissions)366 public <T> T runWithTemporaryPermission(Supplier<T> supplier, String... permissions) { 367 getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(permissions); 368 try { 369 return supplier.get(); 370 } finally { 371 // Revert the permissions needed for the test again. 372 acquireNecessaryPermissions(); 373 } 374 } 375 376 /** 377 * Temporarily assumes the given permissions and executes the given runnable. Reverts any 378 * permissions currently held after the execution. 379 */ runWithTemporaryPermission(Runnable runnable, String... permissions)380 public void runWithTemporaryPermission(Runnable runnable, String... permissions) { 381 getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(permissions); 382 try { 383 runnable.run(); 384 } finally { 385 // Revert the permissions needed for the test again. 386 acquireNecessaryPermissions(); 387 } 388 } 389 390 /** 391 * Temporarily drops any permissions and executes the given supplier. Reverts any permissions 392 * currently held after the execution. 393 */ runWithoutPermissions(Supplier<T> supplier)394 public <T> T runWithoutPermissions(Supplier<T> supplier) { 395 getInstrumentation().getUiAutomation().dropShellPermissionIdentity(); 396 try { 397 return supplier.get(); 398 } finally { 399 // Revert the permissions needed for the test again. 400 acquireNecessaryPermissions(); 401 } 402 } 403 404 /** 405 * Temporarily drops any permissions and executes the given runnable. Reverts any permissions 406 * currently held after the execution. 407 */ runWithoutPermissions(Runnable runnable)408 public void runWithoutPermissions(Runnable runnable) { 409 getInstrumentation().getUiAutomation().dropShellPermissionIdentity(); 410 try { 411 runnable.run(); 412 } finally { 413 // Revert the permissions needed for the test again. 414 acquireNecessaryPermissions(); 415 } 416 } 417 418 /** 419 * Sends the given intent to the given virtual display. 420 */ sendIntentToDisplay(Intent intent, VirtualDisplay virtualDisplay)421 public void sendIntentToDisplay(Intent intent, VirtualDisplay virtualDisplay) { 422 sendIntentToDisplay(intent, virtualDisplay.getDisplay().getDisplayId()); 423 } 424 425 /** 426 * Sends the given intent to the given display. 427 */ sendIntentToDisplay(Intent intent, int displayId)428 public void sendIntentToDisplay(Intent intent, int displayId) { 429 assumeActivityLaunchSupported(displayId); 430 mContext.startActivity(intent, createActivityOptions(displayId)); 431 } 432 433 /** 434 * Starts the activity for the given class on the given virtual display and blocks until it is 435 * successfully launched there. The activity will be finished after the test run. 436 */ startActivityOnDisplaySync( VirtualDisplay virtualDisplay, Class<T> clazz)437 public <T extends Activity> T startActivityOnDisplaySync( 438 VirtualDisplay virtualDisplay, Class<T> clazz) { 439 final int displayId = virtualDisplay.getDisplay().getDisplayId(); 440 return startActivityOnDisplaySync(displayId, clazz); 441 } 442 443 /** 444 * Starts the activity for the given class on the given display and blocks until it is 445 * successfully launched there. The activity will be finished after the test run. 446 */ startActivityOnDisplaySync(int displayId, Class<T> clazz)447 public <T extends Activity> T startActivityOnDisplaySync(int displayId, Class<T> clazz) { 448 return startActivityOnDisplaySync(displayId, new Intent(mContext, clazz) 449 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)); 450 } 451 452 /** 453 * Starts the activity for the given intent on the given virtual display and blocks until it is 454 * successfully launched there. The activity will be finished after the test run. 455 */ startActivityOnDisplaySync( VirtualDisplay virtualDisplay, Intent intent)456 public <T extends Activity> T startActivityOnDisplaySync( 457 VirtualDisplay virtualDisplay, Intent intent) { 458 return startActivityOnDisplaySync(virtualDisplay.getDisplay().getDisplayId(), intent); 459 } 460 461 /** 462 * Starts the activity for the given intent on the given display and blocks until it is 463 * successfully launched there. The activity will be finished after the test run. 464 */ startActivityOnDisplaySync(int displayId, Intent intent)465 public <T extends Activity> T startActivityOnDisplaySync(int displayId, Intent intent) { 466 assumeActivityLaunchSupported(displayId); 467 T activity = (T) getInstrumentation() 468 .startActivitySync(intent, createActivityOptions(displayId)); 469 mTrackerRule.mActivities.add(activity); 470 return activity; 471 } 472 473 /** 474 * Creates activity options for launching activities on the given virtual display. 475 */ createActivityOptions(VirtualDisplay virtualDisplay)476 public static Bundle createActivityOptions(VirtualDisplay virtualDisplay) { 477 return createActivityOptions(virtualDisplay.getDisplay().getDisplayId()); 478 } 479 480 /** 481 * Creates activity options for launching activities on the given display. 482 */ createActivityOptions(int displayId)483 public static Bundle createActivityOptions(int displayId) { 484 return ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(); 485 } 486 487 /** 488 * Skips the test if the device doesn't support virtual displays that can host activities. 489 */ assumeActivityLaunchSupported(int displayId)490 public void assumeActivityLaunchSupported(int displayId) { 491 if (displayId != Display.DEFAULT_DISPLAY) { 492 assumeTrue(FeatureUtil.hasSystemFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS)); 493 // TODO(b/261155110): Re-enable once freeform mode is supported on virtual displays. 494 assumeFalse(FeatureUtil.hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)); 495 } 496 } 497 498 /** 499 * Blocks until the given activity is in resumed state. 500 */ waitAndAssertActivityResumed(ComponentName componentName)501 public void waitAndAssertActivityResumed(ComponentName componentName) { 502 waitAndAssertWindowManagerState("Waiting for activity to be resumed", 503 () -> mWmState.hasActivityState(componentName, WindowManagerState.STATE_RESUMED)); 504 } 505 506 /** 507 * Blocks until the given activity is in resumed state on the given display. 508 */ waitAndAssertActivityResumed(ComponentName componentName, int displayId)509 public void waitAndAssertActivityResumed(ComponentName componentName, int displayId) { 510 waitAndAssertActivityResumed(componentName); 511 mWmState.assertResumedActivities("Activity must be on display " + displayId, 512 mapping -> mapping.put(displayId, componentName)); 513 } 514 515 /** 516 * Blocks until the given activity is gone. 517 */ waitAndAssertActivityRemoved(ComponentName componentName)518 public void waitAndAssertActivityRemoved(ComponentName componentName) { 519 waitAndAssertWindowManagerState("Waiting for activity to be removed", 520 () -> !mWmState.containsActivity(componentName)); 521 } 522 523 /** 524 * Override the default retry limit of WindowManagerStateHelper. 525 * Destroying activities on virtual displays and destroying the virtual displays themselves 526 * takes longer than the default timeout of 5s. 527 */ waitAndAssertWindowManagerState( String message, BooleanSupplier waitCondition)528 private void waitAndAssertWindowManagerState( 529 String message, BooleanSupplier waitCondition) { 530 final Condition<String> condition = 531 new Condition<>(message, () -> { 532 mWmState.computeState(); 533 return waitCondition.getAsBoolean(); 534 }); 535 condition.setRetryLimit(10); 536 assertThat(Condition.waitFor(condition)).isTrue(); 537 } 538 prepareSurface(int width, int height)539 private Surface prepareSurface(int width, int height) { 540 ImageReader imageReader = new ImageReader.Builder(width, height).build(); 541 mTrackerRule.mImageReaders.add(imageReader); 542 return imageReader.getSurface(); 543 } 544 545 /** 546 * Internal rule that tracks all created virtual devices and displays and ensures they are 547 * properly closed and released after the test. 548 */ 549 private static final class VirtualDeviceTrackerRule extends ExternalResource { 550 551 final ArrayList<VirtualDevice> mVirtualDevices = new ArrayList<>(); 552 final ArrayList<VirtualDisplay> mVirtualDisplays = new ArrayList<>(); 553 final ArrayList<Activity> mActivities = new ArrayList<>(); 554 final ArrayList<ImageReader> mImageReaders = new ArrayList<>(); 555 556 @Override after()557 protected void after() { 558 for (Activity activity : mActivities) { 559 activity.finish(); 560 } 561 mActivities.clear(); 562 563 for (VirtualDevice virtualDevice : mVirtualDevices) { 564 virtualDevice.close(); 565 } 566 mVirtualDevices.clear(); 567 for (VirtualDisplay virtualDisplay : mVirtualDisplays) { 568 virtualDisplay.release(); 569 } 570 mVirtualDisplays.clear(); 571 for (ImageReader imageReader : mImageReaders) { 572 imageReader.close(); 573 } 574 mImageReaders.clear(); 575 super.after(); 576 } 577 } 578 } 579