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