CameraDeviceBinderTest.java revision 2100ae7d2b448206813de76ec7e7260e4e765193
1/* 2 * Copyright (C) 2013 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 17package com.android.mediaframeworktest.integration; 18 19import static android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW; 20 21import static org.mockito.Mockito.any; 22import static org.mockito.Mockito.anyLong; 23import static org.mockito.Mockito.argThat; 24import static org.mockito.Mockito.spy; 25import static org.mockito.Mockito.timeout; 26import static org.mockito.Mockito.verify; 27 28import android.graphics.ImageFormat; 29import android.graphics.SurfaceTexture; 30import android.hardware.ICameraService; 31import android.hardware.camera2.CameraCaptureSession; 32import android.hardware.camera2.CameraCharacteristics; 33import android.hardware.camera2.CaptureRequest; 34import android.hardware.camera2.ICameraDeviceCallbacks; 35import android.hardware.camera2.ICameraDeviceUser; 36import android.hardware.camera2.impl.CameraMetadataNative; 37import android.hardware.camera2.impl.CaptureResultExtras; 38import android.hardware.camera2.params.OutputConfiguration; 39import android.hardware.camera2.utils.SubmitInfo; 40import android.media.Image; 41import android.media.ImageReader; 42import android.os.Handler; 43import android.os.HandlerThread; 44import android.os.RemoteException; 45import android.os.ServiceSpecificException; 46import android.os.SystemClock; 47import android.test.AndroidTestCase; 48import android.test.suitebuilder.annotation.SmallTest; 49import android.util.Log; 50import android.view.Surface; 51 52import com.android.mediaframeworktest.MediaFrameworkIntegrationTestRunner; 53 54import org.mockito.ArgumentCaptor; 55import org.mockito.ArgumentMatcher; 56 57public class CameraDeviceBinderTest extends AndroidTestCase { 58 private static String TAG = "CameraDeviceBinderTest"; 59 // Number of streaming callbacks need to check. 60 private static int NUM_CALLBACKS_CHECKED = 10; 61 // Wait for capture result timeout value: 1500ms 62 private final static int WAIT_FOR_COMPLETE_TIMEOUT_MS = 1500; 63 // Wait for flush timeout value: 1000ms 64 private final static int WAIT_FOR_FLUSH_TIMEOUT_MS = 1000; 65 // Wait for idle timeout value: 2000ms 66 private final static int WAIT_FOR_IDLE_TIMEOUT_MS = 2000; 67 // Wait while camera device starts working on requests 68 private final static int WAIT_FOR_WORK_MS = 300; 69 // Default size is VGA, which is mandatory camera supported image size by CDD. 70 private static final int DEFAULT_IMAGE_WIDTH = 640; 71 private static final int DEFAULT_IMAGE_HEIGHT = 480; 72 private static final int MAX_NUM_IMAGES = 5; 73 74 private String mCameraId; 75 private ICameraDeviceUser mCameraUser; 76 private CameraBinderTestUtils mUtils; 77 private ICameraDeviceCallbacks.Stub mMockCb; 78 private Surface mSurface; 79 private OutputConfiguration mOutputConfiguration; 80 private HandlerThread mHandlerThread; 81 private Handler mHandler; 82 ImageReader mImageReader; 83 84 public CameraDeviceBinderTest() { 85 } 86 87 private class ImageDropperListener implements ImageReader.OnImageAvailableListener { 88 89 @Override 90 public void onImageAvailable(ImageReader reader) { 91 Image image = reader.acquireNextImage(); 92 if (image != null) image.close(); 93 } 94 } 95 96 public class DummyCameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { 97 98 /* 99 * (non-Javadoc) 100 * @see 101 * android.hardware.camera2.ICameraDeviceCallbacks#onDeviceError(int, 102 * android.hardware.camera2.CaptureResultExtras) 103 */ 104 public void onDeviceError(int errorCode, CaptureResultExtras resultExtras) 105 throws RemoteException { 106 // TODO Auto-generated method stub 107 108 } 109 110 /* 111 * (non-Javadoc) 112 * @see android.hardware.camera2.ICameraDeviceCallbacks#onDeviceIdle() 113 */ 114 public void onDeviceIdle() throws RemoteException { 115 // TODO Auto-generated method stub 116 117 } 118 119 /* 120 * (non-Javadoc) 121 * @see 122 * android.hardware.camera2.ICameraDeviceCallbacks#onCaptureStarted( 123 * android.hardware.camera2.CaptureResultExtras, long) 124 */ 125 public void onCaptureStarted(CaptureResultExtras resultExtras, long timestamp) 126 throws RemoteException { 127 // TODO Auto-generated method stub 128 129 } 130 131 /* 132 * (non-Javadoc) 133 * @see 134 * android.hardware.camera2.ICameraDeviceCallbacks#onResultReceived( 135 * android.hardware.camera2.impl.CameraMetadataNative, 136 * android.hardware.camera2.CaptureResultExtras) 137 */ 138 public void onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras) 139 throws RemoteException { 140 // TODO Auto-generated method stub 141 142 } 143 144 /* 145 * (non-Javadoc) 146 * @see android.hardware.camera2.ICameraDeviceCallbacks#onPrepared() 147 */ 148 @Override 149 public void onPrepared(int streamId) throws RemoteException { 150 // TODO Auto-generated method stub 151 152 } 153 154 /* 155 * (non-Javadoc) 156 * @see android.hardware.camera2.ICameraDeviceCallbacks#onRequestQueueEmpty() 157 */ 158 @Override 159 public void onRequestQueueEmpty() throws RemoteException { 160 // TODO Auto-generated method stub 161 162 } 163 164 /* 165 * (non-Javadoc) 166 * @see android.hardware.camera2.ICameraDeviceCallbacks#onRepeatingRequestError() 167 */ 168 @Override 169 public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) { 170 // TODO Auto-generated method stub 171 } 172 } 173 174 class IsMetadataNotEmpty implements ArgumentMatcher<CameraMetadataNative> { 175 @Override 176 public boolean matches(CameraMetadataNative obj) { 177 return !obj.isEmpty(); 178 } 179 } 180 181 private void createDefaultSurface() { 182 mImageReader = 183 ImageReader.newInstance(DEFAULT_IMAGE_WIDTH, 184 DEFAULT_IMAGE_HEIGHT, 185 ImageFormat.YUV_420_888, 186 MAX_NUM_IMAGES); 187 mImageReader.setOnImageAvailableListener(new ImageDropperListener(), mHandler); 188 mSurface = mImageReader.getSurface(); 189 mOutputConfiguration = new OutputConfiguration(mSurface); 190 } 191 192 private CaptureRequest.Builder createDefaultBuilder(boolean needStream) throws Exception { 193 CameraMetadataNative metadata = null; 194 assertTrue(metadata.isEmpty()); 195 196 metadata = mCameraUser.createDefaultRequest(TEMPLATE_PREVIEW); 197 assertFalse(metadata.isEmpty()); 198 199 CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false, 200 CameraCaptureSession.SESSION_ID_NONE, mCameraId, /*physicalCameraIdSet*/null); 201 assertFalse(request.isEmpty()); 202 assertFalse(metadata.isEmpty()); 203 if (needStream) { 204 int streamId = mCameraUser.createStream(mOutputConfiguration); 205 assertEquals(0, streamId); 206 request.addTarget(mSurface); 207 } 208 return request; 209 } 210 211 private SubmitInfo submitCameraRequest(CaptureRequest request, boolean streaming) throws Exception { 212 SubmitInfo requestInfo = mCameraUser.submitRequest(request, streaming); 213 assertTrue( 214 "Request IDs should be non-negative (expected: >= 0, actual: " + 215 requestInfo.getRequestId() + ")", 216 requestInfo.getRequestId() >= 0); 217 return requestInfo; 218 } 219 220 @Override 221 protected void setUp() throws Exception { 222 super.setUp(); 223 224 /** 225 * Workaround for mockito and JB-MR2 incompatibility 226 * 227 * Avoid java.lang.IllegalArgumentException: dexcache == null 228 * https://code.google.com/p/dexmaker/issues/detail?id=2 229 */ 230 System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString()); 231 mUtils = new CameraBinderTestUtils(getContext()); 232 233 // This cannot be set in the constructor, since the onCreate isn't 234 // called yet 235 mCameraId = MediaFrameworkIntegrationTestRunner.mCameraId; 236 237 ICameraDeviceCallbacks.Stub dummyCallbacks = new DummyCameraDeviceCallbacks(); 238 239 String clientPackageName = getContext().getPackageName(); 240 241 mMockCb = spy(dummyCallbacks); 242 243 mCameraUser = mUtils.getCameraService().connectDevice(mMockCb, mCameraId, 244 clientPackageName, ICameraService.USE_CALLING_UID); 245 assertNotNull(String.format("Camera %s was null", mCameraId), mCameraUser); 246 mHandlerThread = new HandlerThread(TAG); 247 mHandlerThread.start(); 248 mHandler = new Handler(mHandlerThread.getLooper()); 249 createDefaultSurface(); 250 251 Log.v(TAG, String.format("Camera %s connected", mCameraId)); 252 } 253 254 @Override 255 protected void tearDown() throws Exception { 256 mCameraUser.disconnect(); 257 mCameraUser = null; 258 mSurface.release(); 259 mImageReader.close(); 260 mHandlerThread.quitSafely(); 261 } 262 263 @SmallTest 264 public void testCreateDefaultRequest() throws Exception { 265 CameraMetadataNative metadata = null; 266 assertTrue(metadata.isEmpty()); 267 268 metadata = mCameraUser.createDefaultRequest(TEMPLATE_PREVIEW); 269 assertFalse(metadata.isEmpty()); 270 271 } 272 273 @SmallTest 274 public void testCreateStream() throws Exception { 275 int streamId = mCameraUser.createStream(mOutputConfiguration); 276 assertEquals(0, streamId); 277 278 try { 279 mCameraUser.createStream(mOutputConfiguration); 280 fail("Creating same stream twice"); 281 } catch (ServiceSpecificException e) { 282 assertEquals("Creating same stream twice", 283 e.errorCode, ICameraService.ERROR_ALREADY_EXISTS); 284 } 285 286 mCameraUser.deleteStream(streamId); 287 } 288 289 @SmallTest 290 public void testDeleteInvalidStream() throws Exception { 291 int[] badStreams = { -1, 0, 1, 0xC0FFEE }; 292 for (int badStream : badStreams) { 293 try { 294 mCameraUser.deleteStream(badStream); 295 fail("Allowed bad stream delete"); 296 } catch (ServiceSpecificException e) { 297 assertEquals(e.errorCode, ICameraService.ERROR_ILLEGAL_ARGUMENT); 298 } 299 } 300 } 301 302 @SmallTest 303 public void testCreateStreamTwo() throws Exception { 304 305 // Create first stream 306 int streamId = mCameraUser.createStream(mOutputConfiguration); 307 assertEquals(0, streamId); 308 309 try { 310 mCameraUser.createStream(mOutputConfiguration); 311 fail("Created same stream twice"); 312 } catch (ServiceSpecificException e) { 313 assertEquals("Created same stream twice", 314 ICameraService.ERROR_ALREADY_EXISTS, e.errorCode); 315 } 316 317 // Create second stream with a different surface. 318 SurfaceTexture surfaceTexture = new SurfaceTexture(/* ignored */0); 319 surfaceTexture.setDefaultBufferSize(640, 480); 320 Surface surface2 = new Surface(surfaceTexture); 321 OutputConfiguration output2 = new OutputConfiguration(surface2); 322 323 int streamId2 = mCameraUser.createStream(output2); 324 assertEquals(1, streamId2); 325 326 // Clean up streams 327 mCameraUser.deleteStream(streamId); 328 mCameraUser.deleteStream(streamId2); 329 } 330 331 @SmallTest 332 public void testSubmitBadRequest() throws Exception { 333 334 CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */false); 335 CaptureRequest request1 = builder.build(); 336 try { 337 SubmitInfo requestInfo = mCameraUser.submitRequest(request1, /* streaming */false); 338 fail("Exception expected"); 339 } catch(ServiceSpecificException e) { 340 assertEquals("Expected submitRequest to throw ServiceSpecificException with BAD_VALUE " + 341 "since we had 0 surface targets set.", ICameraService.ERROR_ILLEGAL_ARGUMENT, 342 e.errorCode); 343 } 344 345 builder.addTarget(mSurface); 346 CaptureRequest request2 = builder.build(); 347 try { 348 SubmitInfo requestInfo = mCameraUser.submitRequest(request2, /* streaming */false); 349 fail("Exception expected"); 350 } catch(ServiceSpecificException e) { 351 assertEquals("Expected submitRequest to throw ILLEGAL_ARGUMENT " + 352 "ServiceSpecificException since the target wasn't registered with createStream.", 353 ICameraService.ERROR_ILLEGAL_ARGUMENT, e.errorCode); 354 } 355 } 356 357 @SmallTest 358 public void testSubmitGoodRequest() throws Exception { 359 360 CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */true); 361 CaptureRequest request = builder.build(); 362 363 // Submit valid request twice. 364 SubmitInfo requestInfo1 = submitCameraRequest(request, /* streaming */false); 365 SubmitInfo requestInfo2 = submitCameraRequest(request, /* streaming */false); 366 assertNotSame("Request IDs should be unique for multiple requests", 367 requestInfo1.getRequestId(), requestInfo2.getRequestId()); 368 369 } 370 371 @SmallTest 372 public void testSubmitStreamingRequest() throws Exception { 373 374 CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */true); 375 376 CaptureRequest request = builder.build(); 377 378 // Submit valid request once (non-streaming), and another time 379 // (streaming) 380 SubmitInfo requestInfo1 = submitCameraRequest(request, /* streaming */false); 381 382 SubmitInfo requestInfoStreaming = submitCameraRequest(request, /* streaming */true); 383 assertNotSame("Request IDs should be unique for multiple requests", 384 requestInfo1.getRequestId(), 385 requestInfoStreaming.getRequestId()); 386 387 try { 388 long lastFrameNumber = mCameraUser.cancelRequest(-1); 389 fail("Expected exception"); 390 } catch (ServiceSpecificException e) { 391 assertEquals("Invalid request IDs should not be cancellable", 392 ICameraService.ERROR_ILLEGAL_ARGUMENT, e.errorCode); 393 } 394 395 try { 396 long lastFrameNumber = mCameraUser.cancelRequest(requestInfo1.getRequestId()); 397 fail("Expected exception"); 398 } catch (ServiceSpecificException e) { 399 assertEquals("Non-streaming request IDs should not be cancellable", 400 ICameraService.ERROR_ILLEGAL_ARGUMENT, e.errorCode); 401 } 402 403 long lastFrameNumber = mCameraUser.cancelRequest(requestInfoStreaming.getRequestId()); 404 } 405 406 @SmallTest 407 public void testCameraInfo() throws RemoteException { 408 CameraMetadataNative info = mCameraUser.getCameraInfo(); 409 410 assertFalse(info.isEmpty()); 411 assertNotNull(info.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)); 412 } 413 414 @SmallTest 415 public void testCameraCharacteristics() throws RemoteException { 416 CameraMetadataNative info = mUtils.getCameraService().getCameraCharacteristics(mCameraId); 417 418 assertFalse(info.isEmpty()); 419 assertNotNull(info.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)); 420 } 421 422 @SmallTest 423 public void testWaitUntilIdle() throws Exception { 424 CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */true); 425 SubmitInfo requestInfoStreaming = submitCameraRequest(builder.build(), /* streaming */true); 426 427 // Test Bad case first: waitUntilIdle when there is active repeating request 428 try { 429 mCameraUser.waitUntilIdle(); 430 } catch (ServiceSpecificException e) { 431 assertEquals("waitUntilIdle is invalid operation when there is active repeating request", 432 ICameraService.ERROR_INVALID_OPERATION, e.errorCode); 433 } 434 435 // Test good case, waitUntilIdle when there is no active repeating request 436 long lastFrameNumber = mCameraUser.cancelRequest(requestInfoStreaming.getRequestId()); 437 mCameraUser.waitUntilIdle(); 438 } 439 440 @SmallTest 441 public void testCaptureResultCallbacks() throws Exception { 442 IsMetadataNotEmpty matcher = new IsMetadataNotEmpty(); 443 CaptureRequest request = createDefaultBuilder(/* needStream */true).build(); 444 445 // Test both single request and streaming request. 446 verify(mMockCb, timeout(WAIT_FOR_COMPLETE_TIMEOUT_MS).times(1)).onResultReceived( 447 argThat(matcher), 448 any(CaptureResultExtras.class)); 449 450 verify(mMockCb, timeout(WAIT_FOR_COMPLETE_TIMEOUT_MS).atLeast(NUM_CALLBACKS_CHECKED)) 451 .onResultReceived( 452 argThat(matcher), 453 any(CaptureResultExtras.class)); 454 } 455 456 @SmallTest 457 public void testCaptureStartedCallbacks() throws Exception { 458 CaptureRequest request = createDefaultBuilder(/* needStream */true).build(); 459 460 ArgumentCaptor<Long> timestamps = ArgumentCaptor.forClass(Long.class); 461 462 // Test both single request and streaming request. 463 SubmitInfo requestInfo1 = submitCameraRequest(request, /* streaming */false); 464 verify(mMockCb, timeout(WAIT_FOR_COMPLETE_TIMEOUT_MS).times(1)).onCaptureStarted( 465 any(CaptureResultExtras.class), 466 anyLong()); 467 468 SubmitInfo streamingInfo = submitCameraRequest(request, /* streaming */true); 469 verify(mMockCb, timeout(WAIT_FOR_COMPLETE_TIMEOUT_MS).atLeast(NUM_CALLBACKS_CHECKED)) 470 .onCaptureStarted( 471 any(CaptureResultExtras.class), 472 timestamps.capture()); 473 474 long timestamp = 0; // All timestamps should be larger than 0. 475 for (Long nextTimestamp : timestamps.getAllValues()) { 476 Log.v(TAG, "next t: " + nextTimestamp + " current t: " + timestamp); 477 assertTrue("Captures are out of order", timestamp < nextTimestamp); 478 timestamp = nextTimestamp; 479 } 480 } 481 482 @SmallTest 483 public void testIdleCallback() throws Exception { 484 int status; 485 CaptureRequest request = createDefaultBuilder(/* needStream */true).build(); 486 487 // Try streaming 488 SubmitInfo streamingInfo = submitCameraRequest(request, /* streaming */true); 489 490 // Wait a bit to fill up the queue 491 SystemClock.sleep(WAIT_FOR_WORK_MS); 492 493 // Cancel and make sure we eventually quiesce 494 long lastFrameNumber = mCameraUser.cancelRequest(streamingInfo.getRequestId()); 495 496 verify(mMockCb, timeout(WAIT_FOR_IDLE_TIMEOUT_MS).times(1)).onDeviceIdle(); 497 498 // Submit a few capture requests 499 SubmitInfo requestInfo1 = submitCameraRequest(request, /* streaming */false); 500 SubmitInfo requestInfo2 = submitCameraRequest(request, /* streaming */false); 501 SubmitInfo requestInfo3 = submitCameraRequest(request, /* streaming */false); 502 SubmitInfo requestInfo4 = submitCameraRequest(request, /* streaming */false); 503 SubmitInfo requestInfo5 = submitCameraRequest(request, /* streaming */false); 504 505 // And wait for more idle 506 verify(mMockCb, timeout(WAIT_FOR_IDLE_TIMEOUT_MS).times(2)).onDeviceIdle(); 507 508 } 509 510 @SmallTest 511 public void testFlush() throws Exception { 512 int status; 513 514 // Initial flush should work 515 long lastFrameNumber = mCameraUser.flush(); 516 517 // Then set up a stream 518 CaptureRequest request = createDefaultBuilder(/* needStream */true).build(); 519 520 // Flush should still be a no-op, really 521 lastFrameNumber = mCameraUser.flush(); 522 523 // Submit a few capture requests 524 SubmitInfo requestInfo1 = submitCameraRequest(request, /* streaming */false); 525 SubmitInfo requestInfo2 = submitCameraRequest(request, /* streaming */false); 526 SubmitInfo requestInfo3 = submitCameraRequest(request, /* streaming */false); 527 SubmitInfo requestInfo4 = submitCameraRequest(request, /* streaming */false); 528 SubmitInfo requestInfo5 = submitCameraRequest(request, /* streaming */false); 529 530 // Then flush and wait for idle 531 lastFrameNumber = mCameraUser.flush(); 532 533 verify(mMockCb, timeout(WAIT_FOR_FLUSH_TIMEOUT_MS).times(1)).onDeviceIdle(); 534 535 // Now a streaming request 536 SubmitInfo streamingInfo = submitCameraRequest(request, /* streaming */true); 537 538 // Wait a bit to fill up the queue 539 SystemClock.sleep(WAIT_FOR_WORK_MS); 540 541 // Then flush and wait for the idle callback 542 lastFrameNumber = mCameraUser.flush(); 543 544 verify(mMockCb, timeout(WAIT_FOR_FLUSH_TIMEOUT_MS).times(2)).onDeviceIdle(); 545 546 // TODO: When errors are hooked up, count that errors + successful 547 // requests equal to 5. 548 } 549} 550