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