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