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