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