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("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