Home | History | Annotate | Download | only in integration
      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