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