1 /*
2  * Copyright (C) 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 
17 package android.hardware.camera2.cts.testcases;
18 
19 import static android.hardware.camera2.cts.CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS;
20 import static android.hardware.camera2.cts.CameraTestUtils.PREVIEW_SIZE_BOUND;
21 import static android.hardware.camera2.cts.CameraTestUtils.SESSION_CLOSE_TIMEOUT_MS;
22 import static android.hardware.camera2.cts.CameraTestUtils.SESSION_CONFIGURE_TIMEOUT_MS;
23 import static android.hardware.camera2.cts.CameraTestUtils.SESSION_READY_TIMEOUT_MS;
24 import static android.hardware.camera2.cts.CameraTestUtils.assertFalse;
25 import static android.hardware.camera2.cts.CameraTestUtils.assertNotNull;
26 import static android.hardware.camera2.cts.CameraTestUtils.assertNull;
27 import static android.hardware.camera2.cts.CameraTestUtils.assertTrue;
28 import static android.hardware.camera2.cts.CameraTestUtils.checkSessionConfigurationSupported;
29 import static android.hardware.camera2.cts.CameraTestUtils.configureCameraSession;
30 import static android.hardware.camera2.cts.CameraTestUtils.configureCameraSessionWithConfig;
31 import static android.hardware.camera2.cts.CameraTestUtils.getPreviewSizeBound;
32 import static android.hardware.camera2.cts.CameraTestUtils.getSupportedPreviewSizes;
33 import static android.hardware.camera2.cts.CameraTestUtils.isSessionConfigSupported;
34 
35 import static com.android.ex.camera2.blocking.BlockingSessionCallback.SESSION_CLOSED;
36 import static com.android.ex.camera2.blocking.BlockingSessionCallback.SESSION_READY;
37 import static com.android.ex.camera2.blocking.BlockingStateCallback.STATE_CLOSED;
38 
39 import android.app.Activity;
40 import android.content.Context;
41 import android.content.res.Configuration;
42 import android.graphics.Matrix;
43 import android.graphics.RectF;
44 import android.graphics.SurfaceTexture;
45 import android.hardware.camera2.CameraCaptureSession;
46 import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
47 import android.hardware.camera2.CameraDevice;
48 import android.hardware.camera2.CaptureRequest;
49 import android.hardware.camera2.cts.Camera2MultiViewCtsActivity;
50 import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
51 import android.hardware.camera2.cts.helpers.StaticMetadata;
52 import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
53 import android.hardware.camera2.params.OutputConfiguration;
54 import android.hardware.camera2.params.SessionConfiguration;
55 import android.os.ConditionVariable;
56 import android.os.Handler;
57 import android.os.HandlerThread;
58 import android.os.Looper;
59 import android.util.Log;
60 import android.util.Size;
61 import android.view.Surface;
62 import android.view.TextureView;
63 import android.view.WindowManager;
64 
65 import androidx.test.rule.ActivityTestRule;
66 
67 import com.android.ex.camera2.blocking.BlockingCameraManager;
68 import com.android.ex.camera2.blocking.BlockingSessionCallback;
69 import com.android.ex.camera2.blocking.BlockingStateCallback;
70 
71 import junit.framework.Assert;
72 
73 import org.junit.Rule;
74 
75 import java.util.Arrays;
76 import java.util.HashMap;
77 import java.util.List;
78 
79 /**
80  * Camera2 test case base class by using mixed SurfaceView and TextureView as rendering target.
81  */
82 
83 public class Camera2MultiViewTestCase extends Camera2ParameterizedTestCase {
84     private static final String TAG = "MultiViewTestCase";
85     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
86 
87     protected TextureView[] mTextureView =
88             new TextureView[Camera2MultiViewCtsActivity.MAX_TEXTURE_VIEWS];
89     protected Handler mHandler;
90 
91     private HandlerThread mHandlerThread;
92     private Activity mActivity;
93 
94     private CameraHolder[] mCameraHolders;
95     private HashMap<String, Integer> mCameraIdMap;
96 
97     protected WindowManager mWindowManager;
98 
99     @Rule
100     public ActivityTestRule<Camera2MultiViewCtsActivity> mActivityRule =
101             new ActivityTestRule<>(Camera2MultiViewCtsActivity.class);
102 
103     @Override
setUp()104     public void setUp() throws Exception {
105         super.setUp();
106         mActivity = mActivityRule.getActivity();
107         mHandlerThread = new HandlerThread(TAG);
108         mHandlerThread.start();
109         mHandler = new Handler(mHandlerThread.getLooper());
110         Camera2MultiViewCtsActivity activity = (Camera2MultiViewCtsActivity) mActivity;
111         for (int i = 0; i < Camera2MultiViewCtsActivity.MAX_TEXTURE_VIEWS; i++) {
112             mTextureView[i] = activity.getTextureView(i);
113         }
114         assertNotNull("Unable to get texture view", mTextureView);
115         mCameraIdMap = new HashMap<String, Integer>();
116         String[] cameraIdsUnderTest = getCameraIdsUnderTest();
117         int numCameras = cameraIdsUnderTest.length;
118         mCameraHolders = new CameraHolder[numCameras];
119         for (int i = 0; i < numCameras; i++) {
120             mCameraHolders[i] = new CameraHolder(cameraIdsUnderTest[i]);
121             mCameraIdMap.put(cameraIdsUnderTest[i], i);
122         }
123         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
124     }
125 
126     @Override
tearDown()127     public void tearDown() throws Exception {
128         mHandlerThread.quitSafely();
129         mHandler = null;
130         for (CameraHolder camera : mCameraHolders) {
131             if (camera.isOpened()) {
132                 camera.close();
133                 camera = null;
134             }
135         }
136         super.tearDown();
137     }
138 
139     /**
140      * Update preview TextureView rotation to accommodate discrepancy between preview
141      * buffer and the view window orientation.
142      *
143      * Assumptions:
144      * - Aspect ratio for the sensor buffers is in landscape orientation,
145      * - Dimensions of buffers received are rotated to the natural device orientation.
146      * - The contents of each buffer are rotated by the inverse of the display rotation.
147      * - Surface scales the buffer to fit the current view bounds.
148      * TODO: Make this method works for all orientations
149      *
150      */
updatePreviewDisplayRotation(Size previewSize, TextureView textureView)151     protected void updatePreviewDisplayRotation(Size previewSize, TextureView textureView) {
152         int rotationDegrees = 0;
153         Camera2MultiViewCtsActivity activity = (Camera2MultiViewCtsActivity) mActivity;
154 
155         int displayRotation = activity.getDisplay().getRotation();
156         Configuration config = activity.getResources().getConfiguration();
157 
158         // Get UI display rotation
159         switch (displayRotation) {
160             case Surface.ROTATION_0:
161                 rotationDegrees = 0;
162                 break;
163             case Surface.ROTATION_90:
164                 rotationDegrees = 90;
165             break;
166             case Surface.ROTATION_180:
167                 rotationDegrees = 180;
168             break;
169             case Surface.ROTATION_270:
170                 rotationDegrees = 270;
171             break;
172         }
173 
174         // Get device natural orientation
175         int deviceOrientation = Configuration.ORIENTATION_PORTRAIT;
176         if ((rotationDegrees % 180 == 0 &&
177                 config.orientation == Configuration.ORIENTATION_LANDSCAPE) ||
178                 ((rotationDegrees % 180 != 0 &&
179                 config.orientation == Configuration.ORIENTATION_PORTRAIT))) {
180             deviceOrientation = Configuration.ORIENTATION_LANDSCAPE;
181         }
182 
183         // Rotate the buffer dimensions if device orientation is portrait.
184         int effectiveWidth = previewSize.getWidth();
185         int effectiveHeight = previewSize.getHeight();
186         if (deviceOrientation == Configuration.ORIENTATION_PORTRAIT) {
187             effectiveWidth = previewSize.getHeight();
188             effectiveHeight = previewSize.getWidth();
189         }
190 
191         // Find and center view rect and buffer rect
192         Matrix transformMatrix =  textureView.getTransform(null);
193         int viewWidth = textureView.getWidth();
194         int viewHeight = textureView.getHeight();
195         RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
196         RectF bufRect = new RectF(0, 0, effectiveWidth, effectiveHeight);
197         float centerX = viewRect.centerX();
198         float centerY = viewRect.centerY();
199         bufRect.offset(centerX - bufRect.centerX(), centerY - bufRect.centerY());
200 
201         // Undo ScaleToFit.FILL done by the surface
202         transformMatrix.setRectToRect(viewRect, bufRect, Matrix.ScaleToFit.FILL);
203 
204         // Rotate buffer contents to proper orientation
205         transformMatrix.postRotate((360 - rotationDegrees) % 360, centerX, centerY);
206         if ((rotationDegrees % 180) == 90) {
207             int temp = effectiveWidth;
208             effectiveWidth = effectiveHeight;
209             effectiveHeight = temp;
210         }
211 
212         // Scale to fit view, cropping the longest dimension
213         float scale =
214                 Math.max(viewWidth / (float) effectiveWidth, viewHeight / (float) effectiveHeight);
215         transformMatrix.postScale(scale, scale, centerX, centerY);
216 
217         Handler handler = new Handler(Looper.getMainLooper());
218         class TransformUpdater implements Runnable {
219             TextureView mView;
220             Matrix mTransformMatrix;
221             TransformUpdater(TextureView view, Matrix matrix) {
222                 mView = view;
223                 mTransformMatrix = matrix;
224             }
225 
226             @Override
227             public void run() {
228                 mView.setTransform(mTransformMatrix);
229             }
230         }
231         handler.post(new TransformUpdater(textureView, transformMatrix));
232     }
233 
openCamera(String cameraId)234     protected void openCamera(String cameraId) throws Exception {
235         CameraHolder camera = getCameraHolder(cameraId);
236         assertFalse("Camera has already opened", camera.isOpened());
237         camera.open();
238         return;
239     }
240 
closeCamera(String cameraId)241     protected void closeCamera(String cameraId) throws Exception {
242         CameraHolder camera = getCameraHolder(cameraId);
243         camera.close();
244     }
245 
createSessionWithConfigs(String cameraId, List<OutputConfiguration> configs)246     protected void createSessionWithConfigs(String cameraId, List<OutputConfiguration> configs)
247             throws Exception {
248         CameraHolder camera = getCameraHolder(cameraId);
249         camera.createSessionWithConfigs(configs);
250     }
251 
startPreview( String cameraId, List<Surface> outputSurfaces, CaptureCallback listener)252     protected void startPreview(
253             String cameraId, List<Surface> outputSurfaces, CaptureCallback listener)
254             throws Exception {
255         CameraHolder camera = getCameraHolder(cameraId);
256         assertTrue("Camera " + cameraId + " is not openned", camera.isOpened());
257         camera.startPreview(outputSurfaces, listener);
258     }
259 
startPreviewWithConfigs(String cameraId, List<OutputConfiguration> outputConfigs, CaptureCallback listener)260     protected int startPreviewWithConfigs(String cameraId,
261             List<OutputConfiguration> outputConfigs,
262             CaptureCallback listener)
263             throws Exception {
264         CameraHolder camera = getCameraHolder(cameraId);
265         assertTrue("Camera " + cameraId + " is not openned", camera.isOpened());
266         return camera.startPreviewWithConfigs(outputConfigs, listener);
267     }
268 
stopPreview(String cameraId)269     protected void stopPreview(String cameraId) throws Exception {
270         CameraHolder camera = getCameraHolder(cameraId);
271         assertTrue("Camera " + cameraId + " preview is not running", camera.isPreviewStarted());
272         camera.stopPreview();
273     }
274 
stopRepeating(String cameraId)275     protected void stopRepeating(String cameraId) throws Exception {
276         CameraHolder camera = getCameraHolder(cameraId);
277         assertTrue("Camera " + cameraId + " preview is not running", camera.isPreviewStarted());
278         camera.stopRepeating();
279     }
280 
finalizeOutputConfigs(String cameraId, List<OutputConfiguration> configs, CaptureCallback listener)281     protected void finalizeOutputConfigs(String cameraId, List<OutputConfiguration> configs,
282             CaptureCallback listener) throws Exception {
283         CameraHolder camera = getCameraHolder(cameraId);
284         assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
285         camera.finalizeOutputConfigs(configs, listener);
286     }
287 
updateRepeatingRequest(String cameraId, List<OutputConfiguration> configs, CaptureCallback listener)288     protected int updateRepeatingRequest(String cameraId, List<OutputConfiguration> configs,
289             CaptureCallback listener) throws Exception {
290         CameraHolder camera = getCameraHolder(cameraId);
291         assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
292         return camera.updateRepeatingRequest(configs, listener);
293     }
294 
updateOutputConfiguration(String cameraId, OutputConfiguration config)295     protected void updateOutputConfiguration(String cameraId, OutputConfiguration config)
296             throws Exception {
297         CameraHolder camera = getCameraHolder(cameraId);
298         assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
299         camera.updateOutputConfiguration(config);
300     }
301 
isSessionConfigurationSupported(String cameraId, List<OutputConfiguration> configs)302     protected boolean isSessionConfigurationSupported(String cameraId,
303             List<OutputConfiguration> configs) throws Exception {
304         CameraHolder camera = getCameraHolder(cameraId);
305         assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
306         return camera.isSessionConfigurationSupported(configs);
307     }
308 
capture(String cameraId, CaptureRequest request, CaptureCallback listener)309     protected void capture(String cameraId, CaptureRequest request, CaptureCallback listener)
310             throws Exception {
311         CameraHolder camera = getCameraHolder(cameraId);
312         assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
313         camera.capture(request, listener);
314     }
315 
getCaptureBuilder(String cameraId, int templateId)316     protected CaptureRequest.Builder getCaptureBuilder(String cameraId, int templateId)
317             throws Exception {
318         CameraHolder camera = getCameraHolder(cameraId);
319         assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
320         return camera.getCaptureBuilder(templateId);
321     }
322 
getStaticInfo(String cameraId)323     protected StaticMetadata getStaticInfo(String cameraId) {
324         CameraHolder camera = getCameraHolder(cameraId);
325         StaticMetadata staticInfo = camera.getStaticInfo();
326         assertNotNull("Camera " + cameraId + " static info is null", staticInfo);
327         return staticInfo;
328     }
329 
getOrderedPreviewSizes(String cameraId)330     protected List<Size> getOrderedPreviewSizes(String cameraId) {
331         CameraHolder camera = getCameraHolder(cameraId);
332         assertTrue("Camera is not openned", camera.isOpened());
333         return camera.getOrderedPreviewSizes();
334     }
335 
verifyCreateSessionWithConfigsFailure(String cameraId, List<OutputConfiguration> configs)336     protected void verifyCreateSessionWithConfigsFailure(String cameraId,
337             List<OutputConfiguration> configs) throws Exception {
338         CameraHolder camera = getCameraHolder(cameraId);
339         camera.verifyCreateSessionWithConfigsFailure(configs);
340     }
341 
342     public static class CameraPreviewListener implements TextureView.SurfaceTextureListener {
343         private boolean mFirstPreviewAvailable = false;
344         private float[] mTransformMatrix = new float[16];
345         private final ConditionVariable mPreviewDone = new ConditionVariable();
346 
347         @Override
onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)348         public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
349             // Ignored. The SurfaceTexture is polled by getAvailableSurfaceTexture.
350         }
351 
352         @Override
onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)353         public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
354             // Ignored. The CameraDevice should already know the changed size.
355         }
356 
357         @Override
onSurfaceTextureDestroyed(SurfaceTexture surface)358         public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
359             /**
360              * Return true, assume that client detaches the surface before it is
361              * destroyed. For example, CameraDevice should detach this surface when
362              * stopping preview. No need to release the SurfaceTexture here as it
363              * is released by TextureView after onSurfaceTextureDestroyed is called.
364              */
365             Log.i(TAG, "onSurfaceTextureDestroyed called.");
366             return true;
367         }
368 
369         @Override
onSurfaceTextureUpdated(SurfaceTexture surface)370         public void onSurfaceTextureUpdated(SurfaceTexture surface) {
371             // Invoked every time there's a new Camera preview frame
372             if (!mFirstPreviewAvailable) {
373                 mFirstPreviewAvailable = true;
374                 mPreviewDone.open();
375             }
376 
377             synchronized(this) {
378                 surface.getTransformMatrix(mTransformMatrix);
379             }
380         }
381 
382         /** Waits until the camera preview is up running */
waitForPreviewDone(long timeOutMs)383         public boolean waitForPreviewDone(long timeOutMs) {
384             if (!mPreviewDone.block(timeOutMs)) {
385                 // timeout could be expected or unexpected. The caller will decide.
386                 Log.w(TAG, "waitForPreviewDone timed out after " + timeOutMs + "ms");
387                 return false;
388             }
389             mPreviewDone.close();
390             return true;
391         }
392 
getPreviewTransform()393         public synchronized float[] getPreviewTransform() {
394             return mTransformMatrix;
395         }
396 
397         /** Reset the Listener */
reset()398         public void reset() {
399             mFirstPreviewAvailable = false;
400             mPreviewDone.close();
401         }
402     }
403 
getCameraHolder(String cameraId)404     private CameraHolder getCameraHolder(String cameraId) {
405         Integer cameraIdx = mCameraIdMap.get(cameraId);
406         if (cameraIdx == null) {
407             Assert.fail("Unknown camera Id");
408         }
409         return mCameraHolders[cameraIdx];
410     }
411 
412     // Per device fields
413     private class CameraHolder {
414         private String mCameraId;
415         private CameraStateListener mCameraStateListener;
416         private BlockingStateCallback mBlockingStateListener;
417         private CameraCaptureSession mSession;
418         private CameraDevice mCamera;
419         private StaticMetadata mStaticInfo;
420         private List<Size> mOrderedPreviewSizes;
421         private BlockingSessionCallback mSessionListener;
422 
CameraHolder(String id)423         public CameraHolder(String id){
424             mCameraId = id;
425             mCameraStateListener = new CameraStateListener();
426         }
427 
getStaticInfo()428         public StaticMetadata getStaticInfo() {
429             return mStaticInfo;
430         }
431 
getOrderedPreviewSizes()432         public List<Size> getOrderedPreviewSizes() {
433             return mOrderedPreviewSizes;
434         }
435 
436         class CameraStateListener extends CameraDevice.StateCallback {
437             boolean mDisconnected = false;
438 
439             @Override
onOpened(CameraDevice camera)440             public void onOpened(CameraDevice camera) {
441             }
442 
443             @Override
onDisconnected(CameraDevice camera)444             public void onDisconnected(CameraDevice camera) {
445                 synchronized(this) {
446                     mDisconnected = true;
447                 }
448             }
449 
450             @Override
onError(CameraDevice camera, int error)451             public void onError(CameraDevice camera, int error) {
452             }
453 
isDisconnected()454             public synchronized boolean isDisconnected() {
455                 return mDisconnected;
456             }
457         }
458 
open()459         public void open() throws Exception {
460             assertNull("Camera is already opened", mCamera);
461             mBlockingStateListener = new BlockingStateCallback(mCameraStateListener);
462             mCamera = (new BlockingCameraManager(mCameraManager)).openCamera(
463                     mCameraId, mBlockingStateListener, mHandler);
464             mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(mCameraId),
465                     CheckLevel.ASSERT, /*collector*/null);
466             if (mStaticInfo.isColorOutputSupported()) {
467                 mOrderedPreviewSizes = getSupportedPreviewSizes(
468                         mCameraId, mCameraManager,
469                         getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
470             }
471             assertNotNull(String.format("Failed to open camera device ID: %s", mCameraId), mCamera);
472         }
473 
isOpened()474         public boolean isOpened() {
475             return (mCamera != null && !mCameraStateListener.isDisconnected());
476         }
477 
close()478         public void close() throws Exception {
479             if (!isOpened()) {
480                 return;
481             }
482             mCamera.close();
483             mBlockingStateListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
484             mCamera = null;
485             mSession = null;
486             mStaticInfo = null;
487             mOrderedPreviewSizes = null;
488             mBlockingStateListener = null;
489         }
490 
startPreview(List<Surface> outputSurfaces, CaptureCallback listener)491         public void startPreview(List<Surface> outputSurfaces, CaptureCallback listener)
492                 throws Exception {
493             mSessionListener = new BlockingSessionCallback();
494             mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
495             if (outputSurfaces.isEmpty()) {
496                 return;
497             }
498 
499             // TODO: vary the different settings like crop region to cover more cases.
500             CaptureRequest.Builder captureBuilder =
501                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
502 
503             for (Surface surface : outputSurfaces) {
504                 captureBuilder.addTarget(surface);
505             }
506             mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
507         }
508 
createSessionWithConfigs(List<OutputConfiguration> outputConfigs)509         public void createSessionWithConfigs(List<OutputConfiguration> outputConfigs)
510                 throws Exception {
511             mSessionListener = new BlockingSessionCallback();
512             mSession = configureCameraSessionWithConfig(mCamera, outputConfigs, mSessionListener, mHandler);
513         }
514 
verifyCreateSessionWithConfigsFailure(List<OutputConfiguration> configs)515         public void verifyCreateSessionWithConfigsFailure(List<OutputConfiguration> configs)
516                 throws Exception {
517             BlockingSessionCallback sessionListener = new BlockingSessionCallback();
518             CameraCaptureSession session = configureCameraSessionWithConfig(
519                     mCamera, configs, sessionListener, mHandler);
520 
521             Integer[] sessionStates = {BlockingSessionCallback.SESSION_READY,
522                     BlockingSessionCallback.SESSION_CONFIGURE_FAILED};
523             int state = sessionListener.getStateWaiter().waitForAnyOfStates(
524                     Arrays.asList(sessionStates), SESSION_CONFIGURE_TIMEOUT_MS);
525             assertTrue("Expecting a createSessionWithConfig failure.",
526                     state == BlockingSessionCallback.SESSION_CONFIGURE_FAILED);
527         }
528 
startPreviewWithConfigs(List<OutputConfiguration> outputConfigs, CaptureCallback listener)529         public int startPreviewWithConfigs(List<OutputConfiguration> outputConfigs,
530                 CaptureCallback listener)
531                 throws Exception {
532             checkSessionConfigurationSupported(mCamera, mHandler, outputConfigs,
533                     /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR, mCameraManager,
534                     /*defaultSupport*/ true, "Session configuration query should not fail");
535             createSessionWithConfigs(outputConfigs);
536 
537             CaptureRequest.Builder captureBuilder =
538                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
539 
540             for (OutputConfiguration config : outputConfigs) {
541                 for (Surface surface : config.getSurfaces()) {
542                     captureBuilder.addTarget(surface);
543                 }
544             }
545             return mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
546         }
547 
updateRepeatingRequest(List<OutputConfiguration> configs, CaptureCallback listener)548         public int updateRepeatingRequest(List<OutputConfiguration> configs,
549                 CaptureCallback listener) throws Exception {
550             CaptureRequest.Builder captureBuilder =
551                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
552 
553             for (OutputConfiguration config : configs) {
554                 for (Surface surface : config.getSurfaces()) {
555                     captureBuilder.addTarget(surface);
556                 }
557             }
558             return mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
559         }
560 
updateOutputConfiguration(OutputConfiguration config)561         public void updateOutputConfiguration(OutputConfiguration config) throws Exception {
562             mSession.updateOutputConfiguration(config);
563         }
564 
isSessionConfigurationSupported(List<OutputConfiguration> configs)565         public boolean isSessionConfigurationSupported(List<OutputConfiguration> configs)
566                 throws Exception {
567             return isSessionConfigSupported(mCamera, mHandler, configs,
568                     /*inputConig*/ null, SessionConfiguration.SESSION_REGULAR,
569                     mCameraManager, /*expectedResult*/ true).configSupported;
570         }
571 
capture(CaptureRequest request, CaptureCallback listener)572         public void capture(CaptureRequest request, CaptureCallback listener)
573                 throws Exception {
574             mSession.capture(request, listener, mHandler);
575         }
576 
getCaptureBuilder(int templateId)577         public CaptureRequest.Builder getCaptureBuilder(int templateId) throws Exception {
578             return mCamera.createCaptureRequest(templateId);
579         }
580 
finalizeOutputConfigs(List<OutputConfiguration> configs, CaptureCallback listener)581         public void finalizeOutputConfigs(List<OutputConfiguration> configs,
582                 CaptureCallback listener) throws Exception {
583             mSession.finalizeOutputConfigurations(configs);
584             updateRepeatingRequest(configs, listener);
585         }
586 
isPreviewStarted()587         public boolean isPreviewStarted() {
588             return (mSession != null);
589         }
590 
stopPreview()591         public void stopPreview() throws Exception {
592             if (VERBOSE) Log.v(TAG,
593                     "Stopping camera " + mCameraId +" preview and waiting for idle");
594             if (!isOpened()) {
595                 return;
596             }
597             // Stop repeat, wait for captures to complete, and disconnect from surfaces
598             mSession.close();
599             mSessionListener.getStateWaiter().waitForState(
600                     SESSION_CLOSED, SESSION_CLOSE_TIMEOUT_MS);
601             mSessionListener = null;
602         }
603 
stopRepeating()604         public void stopRepeating() throws Exception {
605             if (VERBOSE) Log.v(TAG,
606                     "Stopping camera " + mCameraId +" repeating request");
607             if (!isOpened()) {
608                 return;
609             }
610             mSession.stopRepeating();
611             mSessionListener.getStateWaiter().waitForState(
612                     SESSION_READY, SESSION_READY_TIMEOUT_MS);
613         }
614     }
615 }
616