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.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