1 /*
2  * Copyright 2014 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 package android.hardware.camera2.cts.helpers;
17 
18 import android.hardware.camera2.CameraAccessException;
19 import android.hardware.camera2.CameraCaptureSession;
20 import android.hardware.camera2.CameraDevice;
21 import android.hardware.camera2.CaptureFailure;
22 import android.hardware.camera2.CaptureRequest;
23 import android.hardware.camera2.TotalCaptureResult;
24 import android.hardware.camera2.cts.CameraTestUtils;
25 import android.os.Handler;
26 import android.util.Log;
27 import android.util.Pair;
28 import android.view.Surface;
29 
30 import com.android.ex.camera2.blocking.BlockingCaptureCallback;
31 import com.android.ex.camera2.blocking.BlockingSessionCallback;
32 import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
33 
34 import junit.framework.Assert;
35 
36 import org.mockito.Mockito;
37 
38 import java.util.List;
39 import java.util.concurrent.LinkedBlockingQueue;
40 
41 import static android.hardware.camera2.cts.helpers.Preconditions.*;
42 import static org.mockito.Mockito.*;
43 
44 /**
45  * A utility class with common functions setting up sessions and capturing.
46  */
47 public class CameraSessionUtils extends Assert {
48     private static final String TAG = "CameraSessionUtils";
49     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
50 
51     /**
52      * A blocking listener class for synchronously opening and configuring sessions.
53      */
54     public static class SessionListener extends BlockingSessionCallback {
55         private final LinkedBlockingQueue<CameraCaptureSession> mSessionQueue =
56                 new LinkedBlockingQueue<>();
57 
58         /**
59          * Get a new configured {@link CameraCaptureSession}.
60          *
61          * <p>
62          * This method is blocking, and will time out after
63          * {@link CameraTestUtils#SESSION_CONFIGURE_TIMEOUT_MS}.
64          * </p>
65          *
66          * @param device the {@link CameraDevice} to open a session for.
67          * @param outputs the {@link Surface} outputs to configure.
68          * @param handler the {@link Handler} to use for callbacks.
69          * @return a configured {@link CameraCaptureSession}.
70          *
71          * @throws CameraAccessException if any of the {@link CameraDevice} methods fail.
72          * @throws TimeoutRuntimeException if no result was received before the timeout.
73          */
getConfiguredSession(CameraDevice device, List<Surface> outputs, Handler handler)74         public synchronized CameraCaptureSession getConfiguredSession(CameraDevice device,
75                                                                       List<Surface> outputs,
76                                                                       Handler handler)
77                 throws CameraAccessException {
78             device.createCaptureSession(outputs, this, handler);
79             getStateWaiter().waitForState(SESSION_CONFIGURED,
80                     CameraTestUtils.SESSION_CONFIGURE_TIMEOUT_MS);
81             return mSessionQueue.poll();
82         }
83 
84         @Override
onConfigured(CameraCaptureSession session)85         public void onConfigured(CameraCaptureSession session) {
86             mSessionQueue.offer(session);
87             super.onConfigured(session);
88         }
89     }
90 
91     /**
92      * A blocking listener class for synchronously capturing and results with a session.
93      */
94     public static class CaptureCallback extends BlockingCaptureCallback {
95         private final LinkedBlockingQueue<TotalCaptureResult> mResultQueue =
96                 new LinkedBlockingQueue<>();
97         private final LinkedBlockingQueue<Long> mCaptureTimeQueue =
98                 new LinkedBlockingQueue<>();
99 
100         /**
101          * Capture a new result with the given {@link CameraCaptureSession}.
102          *
103          * <p>
104          * This method is blocking, and will time out after
105          * {@link CameraTestUtils#CAPTURE_RESULT_TIMEOUT_MS}.
106          * </p>
107          *
108          * @param session the {@link CameraCaptureSession} to use.
109          * @param request the {@link CaptureRequest} to capture with.
110          * @param handler the {@link Handler} to use for callbacks.
111          * @return a {@link Pair} containing the capture result and capture time.
112          *
113          * @throws CameraAccessException if any of the {@link CameraDevice} methods fail.
114          * @throws TimeoutRuntimeException if no result was received before the timeout.
115          */
getCapturedResult( CameraCaptureSession session, CaptureRequest request, Handler handler)116         public synchronized Pair<TotalCaptureResult, Long> getCapturedResult(
117                 CameraCaptureSession session, CaptureRequest request, Handler handler)
118                 throws CameraAccessException {
119             session.capture(request, this, handler);
120             getStateWaiter().waitForState(CAPTURE_COMPLETED,
121                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
122             return new Pair<>(mResultQueue.poll(), mCaptureTimeQueue.poll());
123         }
124 
125         @Override
onCaptureStarted(CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber)126         public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
127                                      long timestamp, long frameNumber) {
128             mCaptureTimeQueue.offer(timestamp);
129             super.onCaptureStarted(session, request, timestamp, frameNumber);
130         }
131 
132         @Override
onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result)133         public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
134                                        TotalCaptureResult result) {
135             mResultQueue.offer(result);
136             super.onCaptureCompleted(session, request, result);
137         }
138     }
139 
140     /**
141      * Get a mocked {@link CaptureCallback}.
142      */
getMockCaptureListener()143     public static CaptureCallback getMockCaptureListener() {
144         return spy(new CaptureCallback());
145     }
146 
147     /**
148      * Get a mocked {@link CaptureCallback}.
149      */
getMockSessionListener()150     public static SessionListener getMockSessionListener() {
151         return spy(new SessionListener());
152     }
153 
154     /**
155      * Configure and return a new {@link CameraCaptureSession}.
156      *
157      * <p>
158      * This will verify that the correct session callbacks are called if a mocked listener is
159      * passed as the {@code listener} argument. This method is blocking, and will time out after
160      * {@link CameraTestUtils#SESSION_CONFIGURE_TIMEOUT_MS}.
161      * </p>
162      *
163      * @param listener a {@link SessionListener} to use for callbacks.
164      * @param device the {@link CameraDevice} to use.
165      * @param outputs the {@link Surface} outputs to configure.
166      * @param handler the {@link Handler} to call callbacks on.
167      * @return a configured {@link CameraCaptureSession}.
168      *
169      * @throws CameraAccessException if any of the {@link CameraDevice} methods fail.
170      * @throws TimeoutRuntimeException if no result was received before the timeout.
171      */
configureAndVerifySession(SessionListener listener, CameraDevice device, List<Surface> outputs, Handler handler)172     public static CameraCaptureSession configureAndVerifySession(SessionListener listener,
173                                                                  CameraDevice device,
174                                                                  List<Surface> outputs,
175                                                                  Handler handler)
176             throws CameraAccessException {
177         checkNotNull(listener);
178         checkNotNull(device);
179         checkNotNull(handler);
180         checkCollectionNotEmpty(outputs, "outputs");
181         checkCollectionElementsNotNull(outputs, "outputs");
182 
183         CameraCaptureSession session = listener.getConfiguredSession(device, outputs, handler);
184         if (Mockito.mockingDetails(listener).isMock()) {
185             verify(listener, never()).onConfigureFailed(any(CameraCaptureSession.class));
186             verify(listener, never()).onClosed(eq(session));
187             verify(listener, atLeastOnce()).onConfigured(eq(session));
188         }
189 
190         checkNotNull(session);
191         return session;
192     }
193 
194     /**
195      * Capture and return a new {@link TotalCaptureResult}.
196      *
197      * <p>
198      * This will verify that the correct capture callbacks are called if a mocked listener is
199      * passed as the {@code listener} argument. This method is blocking, and will time out after
200      * {@link CameraTestUtils#CAPTURE_RESULT_TIMEOUT_MS}.
201      * </p>
202      *
203      * @param listener a {@link CaptureCallback} to use for callbacks.
204      * @param session the {@link CameraCaptureSession} to use.
205      * @param request the {@link CaptureRequest} to capture with.
206      * @param handler the {@link Handler} to call callbacks on.
207      * @return a {@link Pair} containing the capture result and capture time.
208      *
209      * @throws CameraAccessException if any of the {@link CameraDevice} methods fail.
210      * @throws TimeoutRuntimeException if no result was received before the timeout.
211      */
captureAndVerifyResult(CaptureCallback listener, CameraCaptureSession session, CaptureRequest request, Handler handler)212     public static Pair<TotalCaptureResult, Long> captureAndVerifyResult(CaptureCallback listener,
213             CameraCaptureSession session, CaptureRequest request, Handler handler)
214             throws CameraAccessException {
215         checkNotNull(listener);
216         checkNotNull(session);
217         checkNotNull(request);
218         checkNotNull(handler);
219 
220         Pair<TotalCaptureResult, Long> result = listener.getCapturedResult(session, request,
221                 handler);
222         if (Mockito.mockingDetails(listener).isMock()) {
223             verify(listener, never()).onCaptureFailed(any(CameraCaptureSession.class),
224                     any(CaptureRequest.class), any(CaptureFailure.class));
225             verify(listener, atLeastOnce()).onCaptureStarted(eq(session), eq(request),
226                     anyLong(), anyLong());
227             verify(listener, atLeastOnce()).onCaptureCompleted(eq(session), eq(request),
228                     eq(result.first));
229         }
230 
231         checkNotNull(result);
232         return result;
233     }
234 
235     // Suppress default constructor for noninstantiability
CameraSessionUtils()236     private CameraSessionUtils() { throw new AssertionError(); }
237 }
238