1 /* 2 * Copyright (C) 2014 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.hardware.camera2.cts.testcases; 18 19 import static android.hardware.camera2.cts.CameraTestUtils.*; 20 import static com.android.ex.camera2.blocking.BlockingStateCallback.*; 21 22 import android.content.Context; 23 import android.graphics.ImageFormat; 24 import android.graphics.Rect; 25 import android.hardware.camera2.CameraCaptureSession; 26 import android.hardware.camera2.CameraCaptureSession.CaptureCallback; 27 import android.hardware.camera2.CameraCharacteristics; 28 import android.hardware.camera2.CameraDevice; 29 import android.hardware.camera2.CameraManager; 30 import android.hardware.camera2.CaptureRequest; 31 import android.hardware.camera2.params.OutputConfiguration; 32 import android.hardware.camera2.params.SessionConfiguration; 33 import android.util.Size; 34 import android.hardware.camera2.cts.CameraTestUtils; 35 import android.hardware.camera2.cts.helpers.CameraErrorCollector; 36 import android.hardware.camera2.cts.helpers.StaticMetadata; 37 import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel; 38 import android.media.Image; 39 import android.media.Image.Plane; 40 import android.media.ImageReader; 41 import android.os.Handler; 42 import android.os.HandlerThread; 43 import android.test.AndroidTestCase; 44 import android.util.Log; 45 import android.view.Surface; 46 import android.view.WindowManager; 47 48 import com.android.ex.camera2.blocking.BlockingSessionCallback; 49 import com.android.ex.camera2.blocking.BlockingStateCallback; 50 51 import java.io.File; 52 import java.nio.ByteBuffer; 53 import java.util.ArrayList; 54 import java.util.Arrays; 55 import java.util.HashMap; 56 import java.util.List; 57 58 public class Camera2AndroidTestCase extends AndroidTestCase { 59 private static final String TAG = "Camera2AndroidTestCase"; 60 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 61 62 // Default capture size: VGA size is required by CDD. 63 protected static final Size DEFAULT_CAPTURE_SIZE = new Size(640, 480); 64 protected static final int CAPTURE_WAIT_TIMEOUT_MS = 7000; 65 66 protected CameraManager mCameraManager; 67 protected CameraDevice mCamera; 68 protected CameraCaptureSession mCameraSession; 69 protected BlockingSessionCallback mCameraSessionListener; 70 protected BlockingStateCallback mCameraListener; 71 protected String[] mCameraIds; 72 // include both standalone camera IDs and "hidden" physical camera IDs 73 protected String[] mAllCameraIds; 74 protected HashMap<String, StaticMetadata> mAllStaticInfo; 75 protected ImageReader mReader; 76 protected Surface mReaderSurface; 77 protected Handler mHandler; 78 protected HandlerThread mHandlerThread; 79 protected StaticMetadata mStaticInfo; 80 protected CameraErrorCollector mCollector; 81 protected List<Size> mOrderedPreviewSizes; // In descending order. 82 protected List<Size> mOrderedVideoSizes; // In descending order. 83 protected List<Size> mOrderedStillSizes; // In descending order. 84 protected String mDebugFileNameBase; 85 86 protected WindowManager mWindowManager; 87 88 @Override setContext(Context context)89 public void setContext(Context context) { 90 super.setContext(context); 91 mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); 92 assertNotNull("Can't connect to camera manager!", mCameraManager); 93 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); 94 } 95 96 /** 97 * Set up the camera2 test case required environments, including CameraManager, 98 * HandlerThread, Camera IDs, and CameraStateCallback etc. 99 */ 100 @Override setUp()101 protected void setUp() throws Exception { 102 super.setUp(); 103 104 /** 105 * Workaround for mockito and JB-MR2 incompatibility 106 * 107 * Avoid java.lang.IllegalArgumentException: dexcache == null 108 * https://code.google.com/p/dexmaker/issues/detail?id=2 109 */ 110 System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString()); 111 112 mCameraIds = mCameraManager.getCameraIdList(); 113 assertNotNull("Camera ids shouldn't be null", mCameraIds); 114 mHandlerThread = new HandlerThread(TAG); 115 mHandlerThread.start(); 116 mHandler = new Handler(mHandlerThread.getLooper()); 117 mCameraListener = new BlockingStateCallback(); 118 mCollector = new CameraErrorCollector(); 119 120 File filesDir = mContext.getPackageManager().isInstantApp() 121 ? mContext.getFilesDir() 122 : mContext.getExternalFilesDir(null); 123 124 mDebugFileNameBase = filesDir.getPath(); 125 126 mAllStaticInfo = new HashMap<String, StaticMetadata>(); 127 List<String> hiddenPhysicalIds = new ArrayList<>(); 128 for (String cameraId : mCameraIds) { 129 CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId); 130 StaticMetadata staticMetadata = new StaticMetadata(props, 131 CheckLevel.ASSERT, /*collector*/null); 132 mAllStaticInfo.put(cameraId, staticMetadata); 133 134 for (String physicalId : props.getPhysicalCameraIds()) { 135 if (!Arrays.asList(mCameraIds).contains(physicalId) && 136 !hiddenPhysicalIds.contains(physicalId)) { 137 hiddenPhysicalIds.add(physicalId); 138 props = mCameraManager.getCameraCharacteristics(physicalId); 139 staticMetadata = new StaticMetadata( 140 mCameraManager.getCameraCharacteristics(physicalId), 141 CheckLevel.ASSERT, /*collector*/null); 142 mAllStaticInfo.put(physicalId, staticMetadata); 143 } 144 } 145 } 146 mAllCameraIds = new String[mCameraIds.length + hiddenPhysicalIds.size()]; 147 System.arraycopy(mCameraIds, 0, mAllCameraIds, 0, mCameraIds.length); 148 for (int i = 0; i < hiddenPhysicalIds.size(); i++) { 149 mAllCameraIds[mCameraIds.length + i] = hiddenPhysicalIds.get(i); 150 } 151 } 152 153 @Override tearDown()154 protected void tearDown() throws Exception { 155 String[] cameraIdsPostTest = mCameraManager.getCameraIdList(); 156 assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest); 157 Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIds)); 158 Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest)); 159 assertTrue( 160 "Number of cameras changed from " + mCameraIds.length + " to " + 161 cameraIdsPostTest.length, 162 mCameraIds.length == cameraIdsPostTest.length); 163 mHandlerThread.quitSafely(); 164 mHandler = null; 165 closeDefaultImageReader(); 166 167 try { 168 mCollector.verify(); 169 } catch (Throwable e) { 170 // When new Exception(e) is used, exception info will be printed twice. 171 throw new Exception(e.getMessage()); 172 } finally { 173 super.tearDown(); 174 } 175 } 176 177 /** 178 * Start capture with given {@link #CaptureRequest}. 179 * 180 * @param request The {@link #CaptureRequest} to be captured. 181 * @param repeating If the capture is single capture or repeating. 182 * @param listener The {@link #CaptureCallback} camera device used to notify callbacks. 183 * @param handler The handler camera device used to post callbacks. 184 */ startCapture(CaptureRequest request, boolean repeating, CaptureCallback listener, Handler handler)185 protected void startCapture(CaptureRequest request, boolean repeating, 186 CaptureCallback listener, Handler handler) throws Exception { 187 if (VERBOSE) Log.v(TAG, "Starting capture from device"); 188 189 if (repeating) { 190 mCameraSession.setRepeatingRequest(request, listener, handler); 191 } else { 192 mCameraSession.capture(request, listener, handler); 193 } 194 } 195 196 /** 197 * Stop the current active capture. 198 * 199 * @param fast When it is true, {@link CameraDevice#flush} is called, the stop capture 200 * could be faster. 201 */ stopCapture(boolean fast)202 protected void stopCapture(boolean fast) throws Exception { 203 if (VERBOSE) Log.v(TAG, "Stopping capture"); 204 205 if (fast) { 206 /** 207 * Flush is useful for canceling long exposure single capture, it also could help 208 * to make the streaming capture stop sooner. 209 */ 210 mCameraSession.abortCaptures(); 211 mCameraSessionListener.getStateWaiter(). 212 waitForState(BlockingSessionCallback.SESSION_READY, CAMERA_IDLE_TIMEOUT_MS); 213 } else { 214 mCameraSession.close(); 215 mCameraSessionListener.getStateWaiter(). 216 waitForState(BlockingSessionCallback.SESSION_CLOSED, CAMERA_IDLE_TIMEOUT_MS); 217 } 218 } 219 220 /** 221 * Open a {@link #CameraDevice camera device} and get the StaticMetadata for a given camera id. 222 * The default mCameraListener is used to wait for states. 223 * 224 * @param cameraId The id of the camera device to be opened. 225 */ openDevice(String cameraId)226 protected void openDevice(String cameraId) throws Exception { 227 openDevice(cameraId, mCameraListener); 228 } 229 230 /** 231 * Open a {@link #CameraDevice} and get the StaticMetadata for a given camera id and listener. 232 * 233 * @param cameraId The id of the camera device to be opened. 234 * @param listener The {@link #BlockingStateCallback} used to wait for states. 235 */ openDevice(String cameraId, BlockingStateCallback listener)236 protected void openDevice(String cameraId, BlockingStateCallback listener) throws Exception { 237 mCamera = CameraTestUtils.openCamera( 238 mCameraManager, cameraId, listener, mHandler); 239 mCollector.setCameraId(cameraId); 240 mStaticInfo = mAllStaticInfo.get(cameraId); 241 if (mStaticInfo.isColorOutputSupported()) { 242 mOrderedPreviewSizes = getSupportedPreviewSizes( 243 cameraId, mCameraManager, 244 getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND)); 245 mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND); 246 mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null); 247 } 248 249 if (VERBOSE) { 250 Log.v(TAG, "Camera " + cameraId + " is opened"); 251 } 252 } 253 254 /** 255 * Create a {@link #CameraCaptureSession} using the currently open camera. 256 * 257 * @param outputSurfaces The set of output surfaces to configure for this session 258 */ createSession(List<Surface> outputSurfaces)259 protected void createSession(List<Surface> outputSurfaces) throws Exception { 260 mCameraSessionListener = new BlockingSessionCallback(); 261 mCameraSession = CameraTestUtils.configureCameraSession(mCamera, outputSurfaces, 262 mCameraSessionListener, mHandler); 263 } 264 265 /** 266 * Create a {@link #CameraCaptureSession} using the currently open camera with 267 * OutputConfigurations. 268 * 269 * @param outputSurfaces The set of output surfaces to configure for this session 270 */ createSessionByConfigs(List<OutputConfiguration> outputConfigs)271 protected void createSessionByConfigs(List<OutputConfiguration> outputConfigs) throws Exception { 272 mCameraSessionListener = new BlockingSessionCallback(); 273 mCameraSession = CameraTestUtils.configureCameraSessionWithConfig(mCamera, outputConfigs, 274 mCameraSessionListener, mHandler); 275 } 276 277 /** 278 * Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a 279 * given camera id. The default mCameraListener is used to wait for states. 280 * <p> 281 * This function must be used along with the {@link #openDevice} for the 282 * same camera id. 283 * </p> 284 * 285 * @param cameraId The id of the {@link #CameraDevice camera device} to be closed. 286 */ closeDevice(String cameraId)287 protected void closeDevice(String cameraId) { 288 closeDevice(cameraId, mCameraListener); 289 } 290 291 /** 292 * Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a 293 * given camera id and listener. 294 * <p> 295 * This function must be used along with the {@link #openDevice} for the 296 * same camera id. 297 * </p> 298 * 299 * @param cameraId The id of the camera device to be closed. 300 * @param listener The BlockingStateCallback used to wait for states. 301 */ closeDevice(String cameraId, BlockingStateCallback listener)302 protected void closeDevice(String cameraId, BlockingStateCallback listener) { 303 if (mCamera != null) { 304 if (!cameraId.equals(mCamera.getId())) { 305 throw new IllegalStateException("Try to close a device that is not opened yet"); 306 } 307 mCamera.close(); 308 listener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS); 309 mCamera = null; 310 mCameraSession = null; 311 mCameraSessionListener = null; 312 mStaticInfo = null; 313 mOrderedPreviewSizes = null; 314 mOrderedVideoSizes = null; 315 mOrderedStillSizes = null; 316 317 if (VERBOSE) { 318 Log.v(TAG, "Camera " + cameraId + " is closed"); 319 } 320 } 321 } 322 323 /** 324 * Create an {@link ImageReader} object and get the surface. 325 * <p> 326 * This function creates {@link ImageReader} object and surface, then assign 327 * to the default {@link mReader} and {@link mReaderSurface}. It closes the 328 * current default active {@link ImageReader} if it exists. 329 * </p> 330 * 331 * @param size The size of this ImageReader to be created. 332 * @param format The format of this ImageReader to be created 333 * @param maxNumImages The max number of images that can be acquired 334 * simultaneously. 335 * @param listener The listener used by this ImageReader to notify 336 * callbacks. 337 */ createDefaultImageReader(Size size, int format, int maxNumImages, ImageReader.OnImageAvailableListener listener)338 protected void createDefaultImageReader(Size size, int format, int maxNumImages, 339 ImageReader.OnImageAvailableListener listener) throws Exception { 340 closeDefaultImageReader(); 341 342 mReader = createImageReader(size, format, maxNumImages, listener); 343 mReaderSurface = mReader.getSurface(); 344 if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString()); 345 } 346 347 /** 348 * Create an {@link ImageReader} object and get the surface. 349 * <p> 350 * This function creates {@link ImageReader} object and surface, then assign 351 * to the default {@link mReader} and {@link mReaderSurface}. It closes the 352 * current default active {@link ImageReader} if it exists. 353 * </p> 354 * 355 * @param size The size of this ImageReader to be created. 356 * @param format The format of this ImageReader to be created 357 * @param maxNumImages The max number of images that can be acquired 358 * simultaneously. 359 * @param usage The usage flag of the ImageReader 360 * @param listener The listener used by this ImageReader to notify 361 * callbacks. 362 */ createDefaultImageReader(Size size, int format, int maxNumImages, long usage, ImageReader.OnImageAvailableListener listener)363 protected void createDefaultImageReader(Size size, int format, int maxNumImages, long usage, 364 ImageReader.OnImageAvailableListener listener) throws Exception { 365 closeDefaultImageReader(); 366 367 mReader = createImageReader(size, format, maxNumImages, usage, listener); 368 mReaderSurface = mReader.getSurface(); 369 if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString()); 370 } 371 372 /** 373 * Create an {@link ImageReader} object. 374 * 375 * <p>This function creates image reader object for given format, maxImages, and size.</p> 376 * 377 * @param size The size of this ImageReader to be created. 378 * @param format The format of this ImageReader to be created 379 * @param maxNumImages The max number of images that can be acquired simultaneously. 380 * @param listener The listener used by this ImageReader to notify callbacks. 381 */ 382 createImageReader(Size size, int format, int maxNumImages, ImageReader.OnImageAvailableListener listener)383 protected ImageReader createImageReader(Size size, int format, int maxNumImages, 384 ImageReader.OnImageAvailableListener listener) throws Exception { 385 386 ImageReader reader = null; 387 reader = ImageReader.newInstance(size.getWidth(), size.getHeight(), 388 format, maxNumImages); 389 390 reader.setOnImageAvailableListener(listener, mHandler); 391 if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString()); 392 return reader; 393 } 394 395 /** 396 * Create an {@link ImageReader} object. 397 * 398 * <p>This function creates image reader object for given format, maxImages, usage and size.</p> 399 * 400 * @param size The size of this ImageReader to be created. 401 * @param format The format of this ImageReader to be created 402 * @param maxNumImages The max number of images that can be acquired simultaneously. 403 * @param usage The usage flag of the ImageReader 404 * @param listener The listener used by this ImageReader to notify callbacks. 405 */ 406 createImageReader(Size size, int format, int maxNumImages, long usage, ImageReader.OnImageAvailableListener listener)407 protected ImageReader createImageReader(Size size, int format, int maxNumImages, long usage, 408 ImageReader.OnImageAvailableListener listener) throws Exception { 409 ImageReader reader = null; 410 reader = ImageReader.newInstance(size.getWidth(), size.getHeight(), 411 format, maxNumImages, usage); 412 413 reader.setOnImageAvailableListener(listener, mHandler); 414 if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString()); 415 return reader; 416 } 417 418 /** 419 * Close the pending images then close current default {@link ImageReader} object. 420 */ closeDefaultImageReader()421 protected void closeDefaultImageReader() { 422 closeImageReader(mReader); 423 mReader = null; 424 mReaderSurface = null; 425 } 426 427 /** 428 * Close an image reader instance. 429 * 430 * @param reader 431 */ closeImageReader(ImageReader reader)432 protected void closeImageReader(ImageReader reader) { 433 if (reader != null) { 434 try { 435 // Close all possible pending images first. 436 Image image = reader.acquireLatestImage(); 437 if (image != null) { 438 image.close(); 439 } 440 } finally { 441 reader.close(); 442 reader = null; 443 } 444 } 445 } 446 checkImageReaderSessionConfiguration(String msg)447 protected void checkImageReaderSessionConfiguration(String msg) throws Exception { 448 List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>(); 449 outputConfigs.add(new OutputConfiguration(mReaderSurface)); 450 451 checkSessionConfigurationSupported(mCamera, mHandler, outputConfigs, /*inputConfig*/ null, 452 SessionConfiguration.SESSION_REGULAR, /*expectedResult*/ true, msg); 453 } 454 prepareCaptureRequest()455 protected CaptureRequest prepareCaptureRequest() throws Exception { 456 return prepareCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 457 } 458 prepareCaptureRequest(int template)459 protected CaptureRequest prepareCaptureRequest(int template) throws Exception { 460 List<Surface> outputSurfaces = new ArrayList<Surface>(); 461 Surface surface = mReader.getSurface(); 462 assertNotNull("Fail to get surface from ImageReader", surface); 463 outputSurfaces.add(surface); 464 return prepareCaptureRequestForSurfaces(outputSurfaces, template) 465 .build(); 466 } 467 prepareCaptureRequestForSurfaces(List<Surface> surfaces, int template)468 protected CaptureRequest.Builder prepareCaptureRequestForSurfaces(List<Surface> surfaces, 469 int template) 470 throws Exception { 471 createSession(surfaces); 472 473 CaptureRequest.Builder captureBuilder = 474 mCamera.createCaptureRequest(template); 475 assertNotNull("Fail to get captureRequest", captureBuilder); 476 for (Surface surface : surfaces) { 477 captureBuilder.addTarget(surface); 478 } 479 480 return captureBuilder; 481 } 482 prepareCaptureRequestForConfigs( List<OutputConfiguration> outputConfigs, int template)483 protected CaptureRequest.Builder prepareCaptureRequestForConfigs( 484 List<OutputConfiguration> outputConfigs, int template) throws Exception { 485 createSessionByConfigs(outputConfigs); 486 487 CaptureRequest.Builder captureBuilder = 488 mCamera.createCaptureRequest(template); 489 assertNotNull("Fail to get captureRequest", captureBuilder); 490 for (OutputConfiguration config : outputConfigs) { 491 for (Surface s : config.getSurfaces()) { 492 captureBuilder.addTarget(s); 493 } 494 } 495 496 return captureBuilder; 497 } 498 499 /** 500 * Test the invalid Image access: accessing a closed image must result in 501 * {@link IllegalStateException}. 502 * 503 * @param closedImage The closed image. 504 * @param closedBuffer The ByteBuffer from a closed Image. buffer invalid 505 * access will be skipped if it is null. 506 */ imageInvalidAccessTestAfterClose(Image closedImage, Plane closedPlane, ByteBuffer closedBuffer)507 protected void imageInvalidAccessTestAfterClose(Image closedImage, 508 Plane closedPlane, ByteBuffer closedBuffer) { 509 if (closedImage == null) { 510 throw new IllegalArgumentException(" closedImage must be non-null"); 511 } 512 if (closedBuffer != null && !closedBuffer.isDirect()) { 513 throw new IllegalArgumentException("The input ByteBuffer should be direct ByteBuffer"); 514 } 515 516 if (closedPlane != null) { 517 // Plane#getBuffer test 518 try { 519 closedPlane.getBuffer(); // An ISE should be thrown here. 520 fail("Image should throw IllegalStateException when calling getBuffer" 521 + " after the image is closed"); 522 } catch (IllegalStateException e) { 523 // Expected. 524 } 525 526 // Plane#getPixelStride test 527 try { 528 closedPlane.getPixelStride(); // An ISE should be thrown here. 529 fail("Image should throw IllegalStateException when calling getPixelStride" 530 + " after the image is closed"); 531 } catch (IllegalStateException e) { 532 // Expected. 533 } 534 535 // Plane#getRowStride test 536 try { 537 closedPlane.getRowStride(); // An ISE should be thrown here. 538 fail("Image should throw IllegalStateException when calling getRowStride" 539 + " after the image is closed"); 540 } catch (IllegalStateException e) { 541 // Expected. 542 } 543 } 544 545 // ByteBuffer access test 546 if (closedBuffer != null) { 547 try { 548 closedBuffer.get(); // An ISE should be thrown here. 549 fail("Image should throw IllegalStateException when accessing a byte buffer" 550 + " after the image is closed"); 551 } catch (IllegalStateException e) { 552 // Expected. 553 } 554 } 555 556 // Image#getFormat test 557 try { 558 closedImage.getFormat(); 559 fail("Image should throw IllegalStateException when calling getFormat" 560 + " after the image is closed"); 561 } catch (IllegalStateException e) { 562 // Expected. 563 } 564 565 // Image#getWidth test 566 try { 567 closedImage.getWidth(); 568 fail("Image should throw IllegalStateException when calling getWidth" 569 + " after the image is closed"); 570 } catch (IllegalStateException e) { 571 // Expected. 572 } 573 574 // Image#getHeight test 575 try { 576 closedImage.getHeight(); 577 fail("Image should throw IllegalStateException when calling getHeight" 578 + " after the image is closed"); 579 } catch (IllegalStateException e) { 580 // Expected. 581 } 582 583 // Image#getTimestamp test 584 try { 585 closedImage.getTimestamp(); 586 fail("Image should throw IllegalStateException when calling getTimestamp" 587 + " after the image is closed"); 588 } catch (IllegalStateException e) { 589 // Expected. 590 } 591 592 // Image#getTimestamp test 593 try { 594 closedImage.getTimestamp(); 595 fail("Image should throw IllegalStateException when calling getTimestamp" 596 + " after the image is closed"); 597 } catch (IllegalStateException e) { 598 // Expected. 599 } 600 601 // Image#getCropRect test 602 try { 603 closedImage.getCropRect(); 604 fail("Image should throw IllegalStateException when calling getCropRect" 605 + " after the image is closed"); 606 } catch (IllegalStateException e) { 607 // Expected. 608 } 609 610 // Image#setCropRect test 611 try { 612 Rect rect = new Rect(); 613 closedImage.setCropRect(rect); 614 fail("Image should throw IllegalStateException when calling setCropRect" 615 + " after the image is closed"); 616 } catch (IllegalStateException e) { 617 // Expected. 618 } 619 620 // Image#getPlanes test 621 try { 622 closedImage.getPlanes(); 623 fail("Image should throw IllegalStateException when calling getPlanes" 624 + " after the image is closed"); 625 } catch (IllegalStateException e) { 626 // Expected. 627 } 628 } 629 } 630