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