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 
17 package android.hardware.camera2.cts;
18 
19 import static android.hardware.camera2.cts.CameraTestUtils.SessionConfigSupport;
20 import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
21 import static android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
22 import static android.hardware.camera2.cts.CameraTestUtils.StreamCombinationTargets;
23 import static android.hardware.camera2.cts.CameraTestUtils.assertEquals;
24 import static android.hardware.camera2.cts.CameraTestUtils.assertNotNull;
25 import static android.hardware.camera2.cts.CameraTestUtils.assertNull;
26 import static android.hardware.camera2.cts.CameraTestUtils.checkSessionConfigurationSupported;
27 import static android.hardware.camera2.cts.CameraTestUtils.checkSessionConfigurationWithSurfaces;
28 import static android.hardware.camera2.cts.CameraTestUtils.configureReprocessableCameraSession;
29 import static android.hardware.camera2.cts.CameraTestUtils.fail;
30 import static android.hardware.camera2.cts.CameraTestUtils.getUnavailablePhysicalCameras;
31 import static android.hardware.camera2.cts.CameraTestUtils.isSessionConfigSupported;
32 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes;
33 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.JPEG;
34 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.MAXIMUM;
35 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.MAX_RES;
36 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.PREVIEW;
37 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.PRIV;
38 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.RAW;
39 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.RECORD;
40 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.S1440P_4_3;
41 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.S720P;
42 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.USE_CASE_PREVIEW;
43 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.USE_CASE_PREVIEW_VIDEO_STILL;
44 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.USE_CASE_STILL_CAPTURE;
45 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.USE_CASE_VIDEO_CALL;
46 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.USE_CASE_VIDEO_RECORD;
47 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.USE_CASE_CROPPED_RAW;
48 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.VGA;
49 import static android.hardware.camera2.cts.CameraTestUtils.MaxStreamSizes.YUV;
50 
51 import static junit.framework.Assert.assertFalse;
52 import static junit.framework.Assert.assertTrue;
53 
54 import static org.mockito.Mockito.any;
55 import static org.mockito.Mockito.eq;
56 import static org.mockito.Mockito.isA;
57 import static org.mockito.Mockito.mock;
58 import static org.mockito.Mockito.never;
59 import static org.mockito.Mockito.timeout;
60 import static org.mockito.Mockito.verify;
61 
62 import android.graphics.ImageFormat;
63 import android.graphics.Rect;
64 import android.graphics.SurfaceTexture;
65 import android.hardware.camera2.CameraCaptureSession;
66 import android.hardware.camera2.CameraCharacteristics;
67 import android.hardware.camera2.CameraDevice;
68 import android.hardware.camera2.CameraMetadata;
69 import android.hardware.camera2.CaptureFailure;
70 import android.hardware.camera2.CaptureRequest;
71 import android.hardware.camera2.CaptureResult;
72 import android.hardware.camera2.TotalCaptureResult;
73 import android.hardware.camera2.cts.helpers.StaticMetadata;
74 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
75 import android.hardware.camera2.params.DynamicRangeProfiles;
76 import android.hardware.camera2.params.InputConfiguration;
77 import android.hardware.camera2.params.MandatoryStreamCombination;
78 import android.hardware.camera2.params.MandatoryStreamCombination.MandatoryStreamInformation;
79 import android.hardware.camera2.params.OisSample;
80 import android.hardware.camera2.params.OutputConfiguration;
81 import android.hardware.camera2.params.SessionConfiguration;
82 import android.hardware.camera2.params.StreamConfigurationMap;
83 import android.media.Image;
84 import android.media.ImageReader;
85 import android.media.ImageWriter;
86 import android.util.Log;
87 import android.util.Pair;
88 import android.util.Size;
89 import android.view.Surface;
90 
91 import com.android.ex.camera2.blocking.BlockingSessionCallback;
92 
93 import org.junit.Test;
94 import org.junit.runner.RunWith;
95 import org.junit.runners.Parameterized;
96 
97 import java.util.ArrayList;
98 import java.util.Arrays;
99 import java.util.HashSet;
100 import java.util.Iterator;
101 import java.util.List;
102 import java.util.Set;
103 
104 /**
105  * Tests exercising edge cases in camera setup, configuration, and usage.
106  */
107 
108 @RunWith(Parameterized.class)
109 public class RobustnessTest extends Camera2AndroidTestCase {
110     private static final String TAG = "RobustnessTest";
111     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
112 
113     private static final int CONFIGURE_TIMEOUT = 5000; //ms
114     private static final int CAPTURE_TIMEOUT = 1500; //ms
115 
116     // For testTriggerInteractions
117     private static final int PREVIEW_WARMUP_FRAMES = 60;
118     private static final int MAX_RESULT_STATE_CHANGE_WAIT_FRAMES = 100;
119     private static final int MAX_TRIGGER_SEQUENCE_FRAMES = 180; // 6 sec at 30 fps
120     private static final int MAX_RESULT_STATE_POSTCHANGE_WAIT_FRAMES = 10;
121 
122     /**
123      * Test that a {@link CameraCaptureSession} can be configured with a {@link Surface} containing
124      * a dimension other than one of the supported output dimensions.  The buffers produced into
125      * this surface are expected have the dimensions of the closest possible buffer size in the
126      * available stream configurations for a surface with this format.
127      */
128     @Test
testBadSurfaceDimensions()129     public void testBadSurfaceDimensions() throws Exception {
130         for (String id : getCameraIdsUnderTest()) {
131             try {
132                 Log.i(TAG, "Testing Camera " + id);
133                 openDevice(id);
134 
135                 List<Size> testSizes = null;
136                 int format = mStaticInfo.isColorOutputSupported() ?
137                     ImageFormat.YUV_420_888 : ImageFormat.DEPTH16;
138 
139                 testSizes = CameraTestUtils.getSortedSizesForFormat(id, mCameraManager,
140                         format, null);
141 
142                 // Find some size not supported by the camera
143                 Size weirdSize = new Size(643, 577);
144                 int count = 0;
145                 while(testSizes.contains(weirdSize)) {
146                     // Really, they can't all be supported...
147                     weirdSize = new Size(weirdSize.getWidth() + 1, weirdSize.getHeight() + 1);
148                     count++;
149                     assertTrue("Too many exotic YUV_420_888 resolutions supported.", count < 100);
150                 }
151 
152                 // Setup imageReader with invalid dimension
153                 ImageReader imageReader = ImageReader.newInstance(weirdSize.getWidth(),
154                         weirdSize.getHeight(), format, 3);
155 
156                 // Setup ImageReaderListener
157                 SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
158                 imageReader.setOnImageAvailableListener(imageListener, mHandler);
159 
160                 Surface surface = imageReader.getSurface();
161                 List<Surface> surfaces = new ArrayList<>();
162                 surfaces.add(surface);
163 
164                 // Setup a capture request and listener
165                 CaptureRequest.Builder request =
166                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
167                 request.addTarget(surface);
168 
169                 // Check that correct session callback is hit.
170                 CameraCaptureSession.StateCallback sessionListener =
171                         mock(CameraCaptureSession.StateCallback.class);
172                 CameraCaptureSession session = CameraTestUtils.configureCameraSession(mCamera,
173                         surfaces, sessionListener, mHandler);
174 
175                 verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()).
176                         onConfigured(any(CameraCaptureSession.class));
177                 verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()).
178                         onReady(any(CameraCaptureSession.class));
179                 verify(sessionListener, never()).onConfigureFailed(any(CameraCaptureSession.class));
180                 verify(sessionListener, never()).onActive(any(CameraCaptureSession.class));
181                 verify(sessionListener, never()).onClosed(any(CameraCaptureSession.class));
182 
183                 CameraCaptureSession.CaptureCallback captureListener =
184                         mock(CameraCaptureSession.CaptureCallback.class);
185                 session.capture(request.build(), captureListener, mHandler);
186 
187                 verify(captureListener, timeout(CAPTURE_TIMEOUT).atLeastOnce()).
188                         onCaptureCompleted(any(CameraCaptureSession.class),
189                                 any(CaptureRequest.class), any(TotalCaptureResult.class));
190                 verify(captureListener, never()).onCaptureFailed(any(CameraCaptureSession.class),
191                         any(CaptureRequest.class), any(CaptureFailure.class));
192 
193                 Image image = imageListener.getImage(CAPTURE_TIMEOUT);
194                 int imageWidth = image.getWidth();
195                 int imageHeight = image.getHeight();
196                 Size actualSize = new Size(imageWidth, imageHeight);
197 
198                 assertTrue("Camera does not contain outputted image resolution " + actualSize,
199                         testSizes.contains(actualSize));
200                 imageReader.close();
201             } finally {
202                 closeDevice(id);
203             }
204         }
205     }
206 
207     /**
208      * Test for making sure the mandatory stream combinations work as expected.
209      */
210     @Test
testMandatoryOutputCombinations()211     public void testMandatoryOutputCombinations() throws Exception {
212         testMandatoryOutputCombinations(/*maxResolution*/false);
213     }
214 
215     /**
216      * Test for making sure the mandatory stream combinations work as expected.
217      */
testMandatoryOutputCombinations(boolean maxResolution)218     private void testMandatoryOutputCombinations(boolean maxResolution) throws Exception {
219         CameraCharacteristics.Key<MandatoryStreamCombination []> ck =
220                 CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS;
221 
222         if (maxResolution) {
223             ck = CameraCharacteristics.SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS;
224         }
225         String[] cameraIdsUnderTest = getCameraIdsUnderTest();
226         Set<Pair<String, String>> unavailablePhysicalCameras = getUnavailablePhysicalCameras(
227                 mCameraManager, mHandler);
228         for (String id : cameraIdsUnderTest) {
229             openDevice(id);
230             MandatoryStreamCombination[] combinations = mStaticInfo.getCharacteristics().get(ck);
231 
232             if (combinations == null) {
233                 String maxResolutionStr = maxResolution ? " " : " maximum resolution ";
234                 Log.i(TAG, "No mandatory" + maxResolutionStr + "stream combinations for camera: " +
235                         id + " skip test");
236                 closeDevice(id);
237                 continue;
238             }
239 
240             try {
241                 for (MandatoryStreamCombination combination : combinations) {
242                     if (!combination.isReprocessable()) {
243                         if (maxResolution) {
244                             testMandatoryStreamCombination(id, mStaticInfo,
245                                     /*physicalCameraId*/ null, combination, /*substituteY8*/false,
246                                     /*substituteHeic*/false, /*maxResolution*/true);
247                         } else {
248                             testMandatoryStreamCombination(id, mStaticInfo,
249                                     null/*physicalCameraId*/, combination);
250                         }
251                     }
252                 }
253 
254                 // Make sure mandatory stream combinations for each physical camera work
255                 // as expected.
256                 if (mStaticInfo.isLogicalMultiCamera()) {
257                     Set<String> physicalCameraIds =
258                             mStaticInfo.getCharacteristics().getPhysicalCameraIds();
259                     for (String physicalId : physicalCameraIds) {
260                         if (Arrays.asList(cameraIdsUnderTest).contains(physicalId)) {
261                             // If physicalId is advertised in camera ID list, do not need to test
262                             // its stream combination through logical camera.
263                             continue;
264                         }
265                         if (unavailablePhysicalCameras.contains(new Pair<>(id, physicalId))) {
266                             // If physicalId is unavailable, do not attempt to test its
267                             // stream combinations.
268                             continue;
269                         }
270 
271                         StaticMetadata physicalStaticInfo = mAllStaticInfo.get(physicalId);
272 
273                         MandatoryStreamCombination[] phyCombinations =
274                                 physicalStaticInfo.getCharacteristics().get(ck);
275 
276                         if (phyCombinations == null) {
277                             Log.i(TAG, "No mandatory stream combinations for physical camera device: " + id + " skip test");
278                             continue;
279                         }
280 
281                         for (MandatoryStreamCombination combination : phyCombinations) {
282                             if (!combination.isReprocessable()) {
283                                 if (maxResolution) {
284                                    testMandatoryStreamCombination(id, physicalStaticInfo,
285                                            physicalId, combination, /*substituteY8*/false,
286                                            /*substituteHeic*/false, /*maxResolution*/true);
287                                 } else {
288                                     testMandatoryStreamCombination(id, physicalStaticInfo,
289                                             physicalId, combination);
290                                 }
291                             }
292                         }
293                     }
294                 }
295 
296             } finally {
297                 closeDevice(id);
298             }
299         }
300     }
301 
302 
303     /**
304      * Test for making sure the mandatory stream combinations work as expected.
305      */
306     @Test
testMandatoryMaximumResolutionOutputCombinations()307     public void testMandatoryMaximumResolutionOutputCombinations() throws Exception {
308         testMandatoryOutputCombinations(/*maxResolution*/ true);
309     }
310 
311     /**
312      * Test for making sure the mandatory use case stream combinations work as expected.
313      */
314     @Test
testMandatoryUseCaseOutputCombinations()315     public void testMandatoryUseCaseOutputCombinations() throws Exception {
316         for (String id : getCameraIdsUnderTest()) {
317             StaticMetadata info = mAllStaticInfo.get(id);
318             CameraCharacteristics chars = info.getCharacteristics();
319             CameraCharacteristics.Key<MandatoryStreamCombination []> ck =
320                     CameraCharacteristics.SCALER_MANDATORY_USE_CASE_STREAM_COMBINATIONS;
321             MandatoryStreamCombination[] combinations = chars.get(ck);
322 
323             if (!info.isCapabilitySupported(
324                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE)) {
325                 assertNull(combinations);
326                 Log.i(TAG, "Camera id " + id + " doesn't support stream use case, skip test");
327                 continue;
328             }
329 
330             assertNotNull(combinations);
331             openDevice(id);
332 
333             try {
334                 Rect preCorrectionActiveArrayRect = info.getPreCorrectedActiveArraySizeChecked();
335                 for (MandatoryStreamCombination combination : combinations) {
336                     Log.i(TAG, "Testing fixed mandatory output combination with stream use case: " +
337                             combination.getDescription() + " on camera: " + id);
338                     CaptureRequest.Builder requestBuilder =
339                             mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
340                     testMandatoryOutputCombinationWithPresetKeys(id, combination, requestBuilder,
341                             preCorrectionActiveArrayRect);
342                 }
343             } finally {
344                 closeDevice(id);
345             }
346         }
347     }
348 
349     @Test
testMandatoryPreviewStabilizationOutputCombinations()350     public void testMandatoryPreviewStabilizationOutputCombinations() throws Exception {
351         for (String id : getCameraIdsUnderTest()) {
352             StaticMetadata info = mAllStaticInfo.get(id);
353             boolean previewStabilizationSupported = isPreviewStabilizationSupported(info);
354             CameraCharacteristics chars = info.getCharacteristics();
355             CameraCharacteristics.Key<MandatoryStreamCombination []> ck =
356                     CameraCharacteristics
357                             .SCALER_MANDATORY_PREVIEW_STABILIZATION_OUTPUT_STREAM_COMBINATIONS;
358             MandatoryStreamCombination[] combinations = chars.get(ck);
359 
360             if (combinations == null) {
361                 assertFalse("Preview stabilization supported by camera id: " + id
362                         + " but null mandatory streams", previewStabilizationSupported);
363                 Log.i(TAG, "Camera id " + id + " doesn't support preview stabilization, skip test");
364                 continue;
365             } else {
366                 assertTrue("Preview stabilization not supported by camera id: " + id
367                         + " but non-null mandatory streams", previewStabilizationSupported);
368             }
369 
370             openDevice(id);
371 
372             try {
373                 for (MandatoryStreamCombination combination : combinations) {
374                     Log.i(TAG, "Testing fixed mandatory output combination with preview"
375                             + "stabilization case: " + combination.getDescription() + " on camera: "
376                                      + id);
377                     CaptureRequest.Builder requestBuilder =
378                             mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
379                     requestBuilder.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
380                             CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION);
381                     testMandatoryOutputCombinationWithPresetKeys(id, combination, requestBuilder,
382                             /*preCorrectionActiveArrayRect*/null);
383                 }
384             } finally {
385                 closeDevice(id);
386             }
387         }
388     }
389 
isPreviewStabilizationSupported(StaticMetadata info)390     private boolean isPreviewStabilizationSupported(StaticMetadata info) {
391         int[] availableVideoStabilizationModes = info.getAvailableVideoStabilizationModesChecked();
392         if (availableVideoStabilizationModes == null) {
393             return false;
394         }
395         for (int mode : availableVideoStabilizationModes) {
396             if (mode == CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION) {
397                 return true;
398             }
399         }
400         return false;
401     }
402 
testMandatoryOutputCombinationWithPresetKeys(String cameraId, MandatoryStreamCombination combination, CaptureRequest.Builder requestBuilderWithKeys, Rect preCorrectionActiveArrayRect)403     private void testMandatoryOutputCombinationWithPresetKeys(String cameraId,
404             MandatoryStreamCombination combination, CaptureRequest.Builder requestBuilderWithKeys,
405             Rect preCorrectionActiveArrayRect) {
406         final int TIMEOUT_FOR_RESULT_MS = 1000;
407         final int MIN_RESULT_COUNT = 3;
408 
409         // Setup outputs
410         List<OutputConfiguration> outputConfigs = new ArrayList<>();
411         List<Surface> outputSurfaces = new ArrayList<Surface>();
412         List<Surface> uhOutputSurfaces = new ArrayList<Surface>();
413         StreamCombinationTargets targets = new StreamCombinationTargets();
414 
415         CameraTestUtils.setupConfigurationTargets(combination.getStreamsInformation(),
416                 targets, outputConfigs, outputSurfaces, uhOutputSurfaces, MIN_RESULT_COUNT,
417                 /*substituteY8*/ false, /*substituteHeic*/false,
418                 /*physicalCameraId*/ null,
419                 /*multiResStreamConfig*/null, mHandler,
420                 /*dynamicRangeProfiles*/ null);
421 
422         boolean haveSession = false;
423         try {
424             checkSessionConfigurationSupported(mCamera, mHandler, outputConfigs,
425                     /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR,
426                     mCameraManager, true/*defaultSupport*/,
427                     String.format("Session configuration query from combination: %s failed",
428                             combination.getDescription()));
429 
430             createSessionByConfigs(outputConfigs);
431             haveSession = true;
432             for (Surface s : outputSurfaces) {
433                 requestBuilderWithKeys.addTarget(s);
434             }
435             boolean croppedRawUseCase = false;
436             for (OutputConfiguration c : outputConfigs) {
437                 if (c.getStreamUseCase() ==
438                         CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_CROPPED_RAW) {
439                     croppedRawUseCase = true;
440                     break;
441                 }
442             }
443 
444             CaptureRequest request = requestBuilderWithKeys.build();
445             CameraTestUtils.SimpleCaptureCallback captureCallback =
446                     new CameraTestUtils.SimpleCaptureCallback();
447 
448 
449             mCameraSession.setRepeatingRequest(request, captureCallback, mHandler);
450 
451             for (int i = 0; i < MIN_RESULT_COUNT; i++) {
452                 // Makes sure that we received an onCaptureCompleted and not an onCaptureFailed.
453                 TotalCaptureResult result =
454                         captureCallback.getTotalCaptureResultForRequest(request,
455                                 /*numResultsWait*/ 0);
456                 validateResultMandatoryConditions(result, croppedRawUseCase,
457                     preCorrectionActiveArrayRect);
458             }
459             if (captureCallback.hasMoreFailures()) {
460                 mCollector.addMessage("No capture failures expected, but there was a failure");
461             }
462 
463         } catch (Throwable e) {
464             mCollector.addMessage(
465                     String.format("Closing down for combination: %s failed due to: %s",
466                             combination.getDescription(), e.getMessage()));
467         }
468 
469         if (haveSession) {
470             try {
471                 Log.i(TAG, String.format("Done with camera %s, combination: %s, closing session",
472                                 cameraId, combination.getDescription()));
473                 stopCapture(/*fast*/false);
474             } catch (Throwable e) {
475                 mCollector.addMessage(
476                     String.format("Closing down for combination: %s failed due to: %s",
477                             combination.getDescription(), e.getMessage()));
478             }
479         }
480 
481         targets.close();
482     }
483 
validateResultMandatoryConditions(TotalCaptureResult result, boolean croppedRawUseCase, Rect preCorrectionActiveArrayRect)484     private void validateResultMandatoryConditions(TotalCaptureResult result,
485             boolean croppedRawUseCase, Rect preCorrectionActiveArrayRect) {
486         // validate more conditions here
487         if (croppedRawUseCase) {
488             Rect rawCropRegion = result.get(CaptureResult.SCALER_RAW_CROP_REGION);
489             if (rawCropRegion == null) {
490                 mCollector.addMessage("SCALER_RAW_CROP_REGION should not be null " +
491                         "when CROPPED_RAW stream use case is used.");
492             }
493             if (!(preCorrectionActiveArrayRect.width() >= rawCropRegion.width()
494                     && preCorrectionActiveArrayRect.height() >= rawCropRegion.height())) {
495                 mCollector.addMessage("RAW_CROP_REGION dimensions should be <= pre correction"
496                         + " array dimensions. SCALER_RAW_CROP_REGION : "
497                         + rawCropRegion.flattenToString() + " pre correction active array is "
498                         + preCorrectionActiveArrayRect.flattenToString());
499             }
500         }
501     }
502 
testMandatoryStreamCombination(String cameraId, StaticMetadata staticInfo, String physicalCameraId, MandatoryStreamCombination combination)503     private void testMandatoryStreamCombination(String cameraId, StaticMetadata staticInfo,
504             String physicalCameraId, MandatoryStreamCombination combination) throws Exception {
505         // Check whether substituting YUV_888 format with Y8 format
506         boolean substituteY8 = false;
507         if (staticInfo.isMonochromeWithY8()) {
508             List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
509             for (MandatoryStreamInformation streamInfo : streamsInfo) {
510                 if (streamInfo.getFormat() == ImageFormat.YUV_420_888) {
511                     substituteY8 = true;
512                     break;
513                 }
514             }
515         }
516 
517         // Check whether substituting JPEG format with HEIC format
518         boolean substituteHeic = false;
519         if (staticInfo.isHeicSupported()) {
520             List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
521             for (MandatoryStreamInformation streamInfo : streamsInfo) {
522                 if (streamInfo.getFormat() == ImageFormat.JPEG) {
523                     substituteHeic = true;
524                     break;
525                 }
526             }
527         }
528 
529         // Test camera output combination
530         String log = "Testing mandatory stream combination: " + combination.getDescription() +
531                 " on camera: " + cameraId;
532         if (physicalCameraId != null) {
533             log += ", physical sub-camera: " + physicalCameraId;
534         }
535         Log.i(TAG, log);
536         testMandatoryStreamCombination(cameraId, staticInfo, physicalCameraId, combination,
537                 /*substituteY8*/false, /*substituteHeic*/false, /*maxResolution*/false);
538 
539         if (substituteY8) {
540             Log.i(TAG, log + " with Y8");
541             testMandatoryStreamCombination(cameraId, staticInfo, physicalCameraId, combination,
542                     /*substituteY8*/true, /*substituteHeic*/false, /*maxResolution*/false);
543         }
544 
545         if (substituteHeic) {
546             Log.i(TAG, log + " with HEIC");
547             testMandatoryStreamCombination(cameraId, staticInfo, physicalCameraId, combination,
548                     /*substituteY8*/false, /*substituteHeic*/true, /**maxResolution*/ false);
549         }
550     }
551 
testMandatoryStreamCombination(String cameraId, StaticMetadata staticInfo, String physicalCameraId, MandatoryStreamCombination combination, boolean substituteY8, boolean substituteHeic, boolean ultraHighResolution)552     private void testMandatoryStreamCombination(String cameraId,
553             StaticMetadata staticInfo, String physicalCameraId,
554             MandatoryStreamCombination combination,
555             boolean substituteY8, boolean substituteHeic, boolean ultraHighResolution)
556             throws Exception {
557         // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
558         // TODO: This needs to be adjusted based on feedback
559         final int TIMEOUT_MULTIPLIER = ultraHighResolution ? 2 : 1;
560         final int TIMEOUT_FOR_RESULT_MS =
561                 ((staticInfo.isHardwareLevelLegacy()) ? 2000 : 1000) * TIMEOUT_MULTIPLIER;
562         final int MIN_RESULT_COUNT = 3;
563 
564         // Set up outputs
565         List<OutputConfiguration> outputConfigs = new ArrayList<>();
566         List<Surface> outputSurfaces = new ArrayList<Surface>();
567         List<Surface> uhOutputSurfaces = new ArrayList<Surface>();
568         StreamCombinationTargets targets = new StreamCombinationTargets();
569 
570         CameraTestUtils.setupConfigurationTargets(combination.getStreamsInformation(),
571                 targets, outputConfigs, outputSurfaces, uhOutputSurfaces, MIN_RESULT_COUNT,
572                 substituteY8, substituteHeic, physicalCameraId, /*multiResStreamConfig*/null,
573                 mHandler);
574 
575         boolean haveSession = false;
576         try {
577             CaptureRequest.Builder requestBuilder =
578                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
579             CaptureRequest.Builder uhRequestBuilder =
580                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
581 
582             for (Surface s : outputSurfaces) {
583                 requestBuilder.addTarget(s);
584             }
585 
586             for (Surface s : uhOutputSurfaces) {
587                 uhRequestBuilder.addTarget(s);
588             }
589             // We need to explicitly set the sensor pixel mode to default since we're mixing default
590             // and max resolution requests in the same capture session.
591             requestBuilder.set(CaptureRequest.SENSOR_PIXEL_MODE,
592                     CameraMetadata.SENSOR_PIXEL_MODE_DEFAULT);
593             if (ultraHighResolution) {
594                 uhRequestBuilder.set(CaptureRequest.SENSOR_PIXEL_MODE,
595                         CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION);
596             }
597             CameraCaptureSession.CaptureCallback mockCaptureCallback =
598                     mock(CameraCaptureSession.CaptureCallback.class);
599 
600             if (physicalCameraId == null) {
601                 checkSessionConfigurationSupported(mCamera, mHandler, outputConfigs,
602                         /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR,
603                         mCameraManager, true/*defaultSupport*/, String.format(
604                         "Session configuration query from combination: %s failed",
605                         combination.getDescription()));
606             } else {
607                 SessionConfigSupport sessionConfigSupport = isSessionConfigSupported(
608                         mCamera, mHandler, outputConfigs, /*inputConfig*/ null,
609                         SessionConfiguration.SESSION_REGULAR, mCameraManager,
610                         false/*defaultSupport*/);
611                 assertTrue(
612                         String.format("Session configuration query from combination: %s failed",
613                         combination.getDescription()), !sessionConfigSupport.error);
614                 if (!sessionConfigSupport.callSupported) {
615                     return;
616                 }
617                 assertTrue(
618                         String.format("Session configuration must be supported for combination: " +
619                         "%s", combination.getDescription()), sessionConfigSupport.configSupported);
620             }
621 
622             createSessionByConfigs(outputConfigs);
623             haveSession = true;
624             CaptureRequest request = requestBuilder.build();
625             CaptureRequest uhRequest = uhRequestBuilder.build();
626             mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler);
627             if (ultraHighResolution) {
628                 mCameraSession.capture(uhRequest, mockCaptureCallback, mHandler);
629             }
630             verify(mockCaptureCallback,
631                     timeout(TIMEOUT_FOR_RESULT_MS * MIN_RESULT_COUNT).atLeast(MIN_RESULT_COUNT))
632                     .onCaptureCompleted(
633                         eq(mCameraSession),
634                         eq(request),
635                         isA(TotalCaptureResult.class));
636             if (ultraHighResolution) {
637                 verify(mockCaptureCallback,
638                         timeout(TIMEOUT_FOR_RESULT_MS).atLeast(1))
639                         .onCaptureCompleted(
640                             eq(mCameraSession),
641                             eq(uhRequest),
642                             isA(TotalCaptureResult.class));
643             }
644 
645             verify(mockCaptureCallback, never()).
646                     onCaptureFailed(
647                         eq(mCameraSession),
648                         eq(request),
649                         isA(CaptureFailure.class));
650 
651         } catch (Throwable e) {
652             mCollector.addMessage(String.format("Mandatory stream combination: %s failed due: %s",
653                     combination.getDescription(), e.getMessage()));
654         }
655         if (haveSession) {
656             try {
657                 Log.i(TAG, String.format("Done with camera %s, combination: %s, closing session",
658                                 cameraId, combination.getDescription()));
659                 stopCapture(/*fast*/false);
660             } catch (Throwable e) {
661                 mCollector.addMessage(
662                     String.format("Closing down for combination: %s failed due to: %s",
663                             combination.getDescription(), e.getMessage()));
664             }
665         }
666 
667         targets.close();
668     }
669 
670     /**
671      * Test for making sure the required 10-bit stream combinations work as expected.
672      * Since we have too many possible combinations between different 8-bit vs. 10-bit as well
673      * as 10-bit dynamic profiles and in order to maximize the coverage within some reasonable
674      * amount of iterations, the test case will configure 8-bit and 10-bit outputs randomly. In case
675      * we have 10-bit output, then the dynamic range profile will also be randomly picked.
676      */
677     @Test
testMandatory10BitStreamCombinations()678     public void testMandatory10BitStreamCombinations() throws Exception {
679         for (String id : getCameraIdsUnderTest()) {
680             openDevice(id);
681             CameraCharacteristics chars = mStaticInfo.getCharacteristics();
682             if (!CameraTestUtils.hasCapability(
683                     chars, CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT)) {
684                 Log.i(TAG, "Camera id " + id + " doesn't support 10-bit output, skip test");
685                 closeDevice(id);
686                 continue;
687             }
688             CameraCharacteristics.Key<MandatoryStreamCombination []> ck =
689                     CameraCharacteristics.SCALER_MANDATORY_TEN_BIT_OUTPUT_STREAM_COMBINATIONS;
690 
691             MandatoryStreamCombination[] combinations = chars.get(ck);
692             assertNotNull(combinations);
693 
694             try {
695                 for (MandatoryStreamCombination combination : combinations) {
696                     Log.i(TAG, "Testing fixed mandatory 10-bit output stream combination: " +
697                             combination.getDescription() + " on camera: " + id);
698                     DynamicRangeProfiles profiles = mStaticInfo.getCharacteristics().get(
699                             CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES);
700                     assertNotNull(profiles);
701 
702                     // First we want to make sure that a fixed set of 10-bit streams
703                     // is functional
704                     for (Long profile : profiles.getSupportedProfiles()) {
705                         if (profile != DynamicRangeProfiles.STANDARD) {
706                             ArrayList<Long> testProfiles = new ArrayList<Long>();
707                             testProfiles.add(profile);
708                             testMandatory10BitStreamCombination(id, combination, profiles,
709                                     testProfiles);
710                         }
711                     }
712 
713                     Log.i(TAG, "Testing random mandatory 10-bit output stream combination: " +
714                             combination.getDescription() + " on camera: " + id);
715                     // Next try out a random mix of standard 8-bit and 10-bit profiles.
716                     // The number of possible combinations is quite big and testing them
717                     // all on physical hardware can become unfeasible.
718                     ArrayList<Long> testProfiles = new ArrayList<>(
719                             profiles.getSupportedProfiles());
720                     testMandatory10BitStreamCombination(id, combination, profiles, testProfiles);
721                 }
722             } finally {
723                 closeDevice(id);
724             }
725         }
726     }
727 
testMandatory10BitStreamCombination(String cameraId, MandatoryStreamCombination combination, DynamicRangeProfiles profiles, List<Long> testProfiles)728     private void testMandatory10BitStreamCombination(String cameraId,
729             MandatoryStreamCombination combination, DynamicRangeProfiles profiles,
730             List<Long> testProfiles) {
731         final int TIMEOUT_FOR_RESULT_MS = 1000;
732         final int MIN_RESULT_COUNT = 3;
733 
734         // Setup outputs
735         List<OutputConfiguration> outputConfigs = new ArrayList<>();
736         List<Surface> outputSurfaces = new ArrayList<Surface>();
737         List<Surface> uhOutputSurfaces = new ArrayList<Surface>();
738         StreamCombinationTargets targets = new StreamCombinationTargets();
739 
740         CameraTestUtils.setupConfigurationTargets(combination.getStreamsInformation(),
741                 targets, outputConfigs, outputSurfaces, uhOutputSurfaces, MIN_RESULT_COUNT,
742                 /*substituteY8*/ false, /*substituteHeic*/false,
743                 /*physicalCameraId*/ null,
744                 /*multiResStreamConfig*/null, mHandler,
745                 testProfiles);
746 
747         try {
748             checkSessionConfigurationSupported(mCamera, mHandler, outputConfigs,
749                     /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR,
750                     mCameraManager, true/*defaultSupport*/,
751                     String.format("Session configuration query from combination: %s failed",
752                             combination.getDescription()));
753 
754             createSessionByConfigs(outputConfigs);
755 
756             boolean constraintPresent = false;
757             List<Surface> constrainedOutputs = new ArrayList<>(outputSurfaces);
758 
759             while (!outputSurfaces.isEmpty()) {
760                 CaptureRequest.Builder requestBuilder =
761                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
762                 // Check to see how many outputs can be combined in to a single request including
763                 // the first output surface and respecting the advertised constraints
764                 Iterator<OutputConfiguration> it = outputConfigs.iterator();
765                 OutputConfiguration config = it.next();
766                 HashSet<Long> currentProfiles = new HashSet<>();
767                 currentProfiles.add(config.getDynamicRangeProfile());
768                 requestBuilder.addTarget(config.getSurface());
769                 outputSurfaces.remove(config.getSurface());
770                 it.remove();
771                 while (it.hasNext()) {
772                     config = it.next();
773                     Long currentProfile = config.getDynamicRangeProfile();
774                     Set<Long> newLimitations = profiles.getProfileCaptureRequestConstraints(
775                             currentProfile);
776                     if (newLimitations.isEmpty() || (newLimitations.containsAll(currentProfiles))) {
777                         currentProfiles.add(currentProfile);
778                         requestBuilder.addTarget(config.getSurface());
779                         outputSurfaces.remove(config.getSurface());
780                         it.remove();
781                     } else if (!constraintPresent && !newLimitations.isEmpty() &&
782                             !newLimitations.containsAll(currentProfiles)) {
783                         constraintPresent = true;
784                     }
785                 }
786 
787                 CaptureRequest request = requestBuilder.build();
788                 CameraCaptureSession.CaptureCallback mockCaptureCallback =
789                         mock(CameraCaptureSession.CaptureCallback.class);
790                 mCameraSession.capture(request, mockCaptureCallback, mHandler);
791                 verify(mockCaptureCallback,
792                         timeout(TIMEOUT_FOR_RESULT_MS).atLeastOnce())
793                         .onCaptureCompleted(
794                                 eq(mCameraSession),
795                                 eq(request),
796                                 isA(TotalCaptureResult.class));
797 
798                 verify(mockCaptureCallback, never()).
799                         onCaptureFailed(
800                                 eq(mCameraSession),
801                                 eq(request),
802                                 isA(CaptureFailure.class));
803             }
804 
805             if (constraintPresent) {
806                 // Capture requests that include output surfaces with dynamic range profiles that
807                 // cannot be combined must throw a corresponding exception
808                 CaptureRequest.Builder requestBuilder =
809                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
810                 for (Surface s : constrainedOutputs) {
811                     requestBuilder.addTarget(s);
812                 }
813 
814                 CaptureRequest request = requestBuilder.build();
815                 CameraCaptureSession.CaptureCallback mockCaptureCallback =
816                         mock(CameraCaptureSession.CaptureCallback.class);
817                 try {
818                     mCameraSession.capture(request, mockCaptureCallback, mHandler);
819                     fail("Capture request to outputs with incompatible dynamic range profiles "
820                             + "must always fail!");
821                 } catch (IllegalArgumentException e) {
822                     // Expected
823                 }
824             }
825 
826             Log.i(TAG, String.format("Done with camera %s, combination: %s, closing session",
827                     cameraId, combination.getDescription()));
828         } catch (Throwable e) {
829             mCollector.addMessage(
830                     String.format("Closing down for combination: %s failed due to: %s",
831                             combination.getDescription(), e.getMessage()));
832         }
833 
834         targets.close();
835     }
836 
837     /**
838      * Test for making sure the required reprocess input/output combinations for each hardware
839      * level and capability work as expected.
840      */
841     @Test
testMandatoryReprocessConfigurations()842     public void testMandatoryReprocessConfigurations() throws Exception {
843         testMandatoryReprocessConfigurations(/*maxResolution*/false);
844     }
845 
846     /**
847      * Test for making sure the required reprocess input/output combinations for each hardware
848      * level and capability work as expected.
849      */
850     @Test
testMandatoryMaximumResolutionReprocessConfigurations()851     public void testMandatoryMaximumResolutionReprocessConfigurations() throws Exception {
852         testMandatoryReprocessConfigurations(/*maxResolution*/true);
853     }
854 
855     /**
856      * Test for making sure the required reprocess input/output combinations for each hardware
857      * level and capability work as expected.
858      */
testMandatoryReprocessConfigurations(boolean maxResolution)859     public void testMandatoryReprocessConfigurations(boolean maxResolution) throws Exception {
860         for (String id : getCameraIdsUnderTest()) {
861             openDevice(id);
862             CameraCharacteristics chars = mStaticInfo.getCharacteristics();
863             if (maxResolution && !CameraTestUtils.hasCapability(
864                   chars, CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING)) {
865                 Log.i(TAG, "Camera id " + id + "doesn't support REMOSAIC_REPROCESSING, skip test");
866                 closeDevice(id);
867                 continue;
868             }
869             CameraCharacteristics.Key<MandatoryStreamCombination []> ck =
870                     CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS;
871 
872             if (maxResolution) {
873                 ck = CameraCharacteristics.SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS;
874             }
875 
876             MandatoryStreamCombination[] combinations = chars.get(ck);
877             if (combinations == null) {
878                 Log.i(TAG, "No mandatory stream combinations for camera: " + id + " skip test");
879                 closeDevice(id);
880                 continue;
881             }
882 
883             try {
884                 for (MandatoryStreamCombination combination : combinations) {
885                     if (combination.isReprocessable()) {
886                         Log.i(TAG, "Testing mandatory reprocessable stream combination: " +
887                                 combination.getDescription() + " on camera: " + id);
888                         testMandatoryReprocessableStreamCombination(id, combination, maxResolution);
889                     }
890                 }
891             } finally {
892                 closeDevice(id);
893             }
894         }
895     }
896 
testMandatoryReprocessableStreamCombination(String cameraId, MandatoryStreamCombination combination, boolean maxResolution)897     private void testMandatoryReprocessableStreamCombination(String cameraId,
898             MandatoryStreamCombination combination, boolean maxResolution)  throws Exception {
899         // Test reprocess stream combination
900         testMandatoryReprocessableStreamCombination(cameraId, combination,
901                 /*substituteY8*/false, /*substituteHeic*/false, maxResolution/*maxResolution*/);
902         if (maxResolution) {
903             // Maximum resolution mode doesn't guarantee HEIC and Y8 streams.
904             return;
905         }
906 
907         // Test substituting YUV_888 format with Y8 format in reprocess stream combination.
908         if (mStaticInfo.isMonochromeWithY8()) {
909             List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
910             boolean substituteY8 = false;
911             for (MandatoryStreamInformation streamInfo : streamsInfo) {
912                 if (streamInfo.getFormat() == ImageFormat.YUV_420_888) {
913                     substituteY8 = true;
914                 }
915             }
916             if (substituteY8) {
917                 testMandatoryReprocessableStreamCombination(cameraId, combination,
918                         /*substituteY8*/true, /*substituteHeic*/false, false/*maxResolution*/);
919             }
920         }
921 
922         if (mStaticInfo.isHeicSupported()) {
923             List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
924             boolean substituteHeic = false;
925             for (MandatoryStreamInformation streamInfo : streamsInfo) {
926                 if (streamInfo.getFormat() == ImageFormat.JPEG) {
927                     substituteHeic = true;
928                 }
929             }
930             if (substituteHeic) {
931                 testMandatoryReprocessableStreamCombination(cameraId, combination,
932                         /*substituteY8*/false, /*substituteHeic*/true, false/*maxResolution*/);
933             }
934         }
935     }
936 
testMandatoryReprocessableStreamCombination(String cameraId, MandatoryStreamCombination combination, boolean substituteY8, boolean substituteHeic, boolean maxResolution)937     private void testMandatoryReprocessableStreamCombination(String cameraId,
938             MandatoryStreamCombination combination, boolean substituteY8,
939             boolean substituteHeic, boolean maxResolution) throws Exception {
940 
941         final int TIMEOUT_MULTIPLIER = maxResolution ? 2 : 1;
942         final int TIMEOUT_FOR_RESULT_MS = 5000 * TIMEOUT_MULTIPLIER;
943         final int NUM_REPROCESS_CAPTURES_PER_CONFIG = 3;
944 
945         StreamCombinationTargets targets = new StreamCombinationTargets();
946         ArrayList<Surface> defaultOutputSurfaces = new ArrayList<>();
947         ArrayList<Surface> allOutputSurfaces = new ArrayList<>();
948         List<OutputConfiguration> outputConfigs = new ArrayList<>();
949         List<Surface> uhOutputSurfaces = new ArrayList<Surface>();
950         ImageReader inputReader = null;
951         ImageWriter inputWriter = null;
952         SimpleImageReaderListener inputReaderListener = new SimpleImageReaderListener();
953         SimpleCaptureCallback inputCaptureListener = new SimpleCaptureCallback();
954         SimpleCaptureCallback reprocessOutputCaptureListener = new SimpleCaptureCallback();
955 
956         List<MandatoryStreamInformation> streamInfo = combination.getStreamsInformation();
957         assertTrue("Reprocessable stream combinations should have at least 3 or more streams",
958                 (streamInfo != null) && (streamInfo.size() >= 3));
959 
960         assertTrue("The first mandatory stream information in a reprocessable combination must " +
961                 "always be input", streamInfo.get(0).isInput());
962 
963         List<Size> inputSizes = streamInfo.get(0).getAvailableSizes();
964         int inputFormat = streamInfo.get(0).getFormat();
965         if (substituteY8 && (inputFormat == ImageFormat.YUV_420_888)) {
966             inputFormat = ImageFormat.Y8;
967         }
968 
969         Log.i(TAG, "testMandatoryReprocessableStreamCombination: " +
970                 combination.getDescription() + ", substituteY8 = " + substituteY8 +
971                 ", substituteHeic = " + substituteHeic);
972         try {
973             // The second stream information entry is the ZSL stream, which is configured
974             // separately.
975             List<MandatoryStreamInformation> mandatoryStreamInfos = null;
976             mandatoryStreamInfos = new ArrayList<MandatoryStreamInformation>();
977             mandatoryStreamInfos = streamInfo.subList(2, streamInfo.size());
978             CameraTestUtils.setupConfigurationTargets(mandatoryStreamInfos, targets,
979                     outputConfigs, defaultOutputSurfaces, uhOutputSurfaces,
980                     NUM_REPROCESS_CAPTURES_PER_CONFIG,
981                     substituteY8, substituteHeic, null/*overridePhysicalCameraId*/,
982                     /*multiResStreamConfig*/null, mHandler);
983             allOutputSurfaces.addAll(defaultOutputSurfaces);
984             allOutputSurfaces.addAll(uhOutputSurfaces);
985             InputConfiguration inputConfig = new InputConfiguration(inputSizes.get(0).getWidth(),
986                     inputSizes.get(0).getHeight(), inputFormat);
987 
988             // For each config, YUV and JPEG outputs will be tested. (For YUV/Y8 reprocessing,
989             // the YUV/Y8 ImageReader for input is also used for output.)
990             final boolean inputIsYuv = inputConfig.getFormat() == ImageFormat.YUV_420_888;
991             final boolean inputIsY8 = inputConfig.getFormat() == ImageFormat.Y8;
992             final boolean useYuv = inputIsYuv || targets.mYuvTargets.size() > 0;
993             final boolean useY8 = inputIsY8 || targets.mY8Targets.size() > 0;
994             final int totalNumReprocessCaptures =  NUM_REPROCESS_CAPTURES_PER_CONFIG *
995                     (maxResolution ? 1 : (((inputIsYuv || inputIsY8) ? 1 : 0) +
996                     (substituteHeic ? targets.mHeicTargets.size() : targets.mJpegTargets.size()) +
997                     (useYuv ? targets.mYuvTargets.size() : targets.mY8Targets.size())));
998 
999             // It needs 1 input buffer for each reprocess capture + the number of buffers
1000             // that will be used as outputs.
1001             inputReader = ImageReader.newInstance(inputConfig.getWidth(), inputConfig.getHeight(),
1002                     inputConfig.getFormat(),
1003                     totalNumReprocessCaptures + NUM_REPROCESS_CAPTURES_PER_CONFIG);
1004             inputReader.setOnImageAvailableListener(inputReaderListener, mHandler);
1005             allOutputSurfaces.add(inputReader.getSurface());
1006 
1007             checkSessionConfigurationWithSurfaces(mCamera, mHandler, allOutputSurfaces,
1008                     inputConfig, SessionConfiguration.SESSION_REGULAR, mCameraManager,
1009                     /*defaultSupport*/ true, String.format("Session configuration query %s failed",
1010                     combination.getDescription()));
1011 
1012             // Verify we can create a reprocessable session with the input and all outputs.
1013             BlockingSessionCallback sessionListener = new BlockingSessionCallback();
1014             CameraCaptureSession session = configureReprocessableCameraSession(mCamera,
1015                     inputConfig, allOutputSurfaces, sessionListener, mHandler);
1016             inputWriter = ImageWriter.newInstance(session.getInputSurface(),
1017                     totalNumReprocessCaptures);
1018 
1019             // Prepare a request for reprocess input
1020             CaptureRequest.Builder builder = mCamera.createCaptureRequest(
1021                     CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
1022             builder.addTarget(inputReader.getSurface());
1023             if (maxResolution) {
1024                 builder.set(CaptureRequest.SENSOR_PIXEL_MODE,
1025                         CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION);
1026             }
1027 
1028             for (int i = 0; i < totalNumReprocessCaptures; i++) {
1029                 session.capture(builder.build(), inputCaptureListener, mHandler);
1030             }
1031 
1032             List<CaptureRequest> reprocessRequests = new ArrayList<>();
1033             List<Surface> reprocessOutputs = new ArrayList<>();
1034 
1035             if (maxResolution) {
1036                 if (uhOutputSurfaces.size() == 0) { // RAW -> RAW reprocessing
1037                     reprocessOutputs.add(inputReader.getSurface());
1038                 } else {
1039                     for (Surface surface : uhOutputSurfaces) {
1040                         reprocessOutputs.add(surface);
1041                     }
1042                 }
1043             } else {
1044                 if (inputIsYuv || inputIsY8) {
1045                     reprocessOutputs.add(inputReader.getSurface());
1046                 }
1047 
1048                 for (ImageReader reader : targets.mJpegTargets) {
1049                     reprocessOutputs.add(reader.getSurface());
1050                 }
1051 
1052                 for (ImageReader reader : targets.mHeicTargets) {
1053                     reprocessOutputs.add(reader.getSurface());
1054                 }
1055 
1056                 for (ImageReader reader : targets.mYuvTargets) {
1057                     reprocessOutputs.add(reader.getSurface());
1058                 }
1059 
1060                 for (ImageReader reader : targets.mY8Targets) {
1061                     reprocessOutputs.add(reader.getSurface());
1062                 }
1063             }
1064 
1065             for (int i = 0; i < NUM_REPROCESS_CAPTURES_PER_CONFIG; i++) {
1066                 for (Surface output : reprocessOutputs) {
1067                     TotalCaptureResult result = inputCaptureListener.getTotalCaptureResult(
1068                             TIMEOUT_FOR_RESULT_MS);
1069                     builder =  mCamera.createReprocessCaptureRequest(result);
1070                     inputWriter.queueInputImage(
1071                             inputReaderListener.getImage(TIMEOUT_FOR_RESULT_MS));
1072                     builder.addTarget(output);
1073                     reprocessRequests.add(builder.build());
1074                 }
1075             }
1076 
1077             session.captureBurst(reprocessRequests, reprocessOutputCaptureListener, mHandler);
1078 
1079             for (int i = 0; i < reprocessOutputs.size() * NUM_REPROCESS_CAPTURES_PER_CONFIG; i++) {
1080                 TotalCaptureResult result = reprocessOutputCaptureListener.getTotalCaptureResult(
1081                         TIMEOUT_FOR_RESULT_MS);
1082             }
1083         } catch (Throwable e) {
1084             mCollector.addMessage(String.format("Reprocess stream combination %s failed due to: %s",
1085                     combination.getDescription(), e.getMessage()));
1086         } finally {
1087             inputReaderListener.drain();
1088             reprocessOutputCaptureListener.drain();
1089             targets.close();
1090 
1091             if (inputReader != null) {
1092                 inputReader.close();
1093             }
1094 
1095             if (inputWriter != null) {
1096                 inputWriter.close();
1097             }
1098         }
1099     }
1100 
1101     @Test
testBasicTriggerSequence()1102     public void testBasicTriggerSequence() throws Exception {
1103 
1104         for (String id : getCameraIdsUnderTest()) {
1105             Log.i(TAG, String.format("Testing Camera %s", id));
1106 
1107             try {
1108                 // Legacy devices do not support precapture trigger; don't test devices that
1109                 // can't focus
1110                 StaticMetadata staticInfo = mAllStaticInfo.get(id);
1111                 if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) {
1112                     continue;
1113                 }
1114                 // Depth-only devices won't support AE
1115                 if (!staticInfo.isColorOutputSupported()) {
1116                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
1117                     continue;
1118                 }
1119 
1120                 openDevice(id);
1121                 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
1122                 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
1123 
1124                 for (int afMode : availableAfModes) {
1125                     if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
1126                             afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
1127                         // Only test AF modes that have meaningful trigger behavior
1128                         continue;
1129                     }
1130 
1131                     for (int aeMode : availableAeModes) {
1132                         if (aeMode ==  CameraCharacteristics.CONTROL_AE_MODE_OFF) {
1133                             // Only test AE modes that have meaningful trigger behavior
1134                             continue;
1135                         }
1136 
1137                         SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
1138 
1139                         CaptureRequest.Builder previewRequest =
1140                                 prepareTriggerTestSession(preview, aeMode, afMode);
1141 
1142                         SimpleCaptureCallback captureListener =
1143                                 new CameraTestUtils.SimpleCaptureCallback();
1144 
1145                         mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener,
1146                                 mHandler);
1147 
1148                         // Cancel triggers
1149 
1150                         cancelTriggersAndWait(previewRequest, captureListener, afMode);
1151 
1152                         //
1153                         // Standard sequence - AF trigger then AE trigger
1154 
1155                         if (VERBOSE) {
1156                             Log.v(TAG, String.format("Triggering AF"));
1157                         }
1158 
1159                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
1160                                 CaptureRequest.CONTROL_AF_TRIGGER_START);
1161                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
1162                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
1163 
1164                         CaptureRequest triggerRequest = previewRequest.build();
1165                         mCameraSession.capture(triggerRequest, captureListener, mHandler);
1166 
1167                         CaptureResult triggerResult = captureListener.getCaptureResultForRequest(
1168                                 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
1169                         int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
1170                         boolean focusComplete = false;
1171 
1172                         for (int i = 0;
1173                              i < MAX_TRIGGER_SEQUENCE_FRAMES && !focusComplete;
1174                              i++) {
1175 
1176                             focusComplete = verifyAfSequence(afMode, afState, focusComplete);
1177 
1178                             CaptureResult focusResult = captureListener.getCaptureResult(
1179                                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
1180                             afState = focusResult.get(CaptureResult.CONTROL_AF_STATE);
1181                         }
1182 
1183                         assertTrue("Focusing never completed!", focusComplete);
1184 
1185                         // Standard sequence - Part 2 AE trigger
1186 
1187                         if (VERBOSE) {
1188                             Log.v(TAG, String.format("Triggering AE"));
1189                         }
1190 
1191                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
1192                                 CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
1193                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
1194                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
1195 
1196                         triggerRequest = previewRequest.build();
1197                         mCameraSession.capture(triggerRequest, captureListener, mHandler);
1198 
1199                         triggerResult = captureListener.getCaptureResultForRequest(
1200                                 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
1201 
1202                         int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE);
1203 
1204                         boolean precaptureComplete = false;
1205 
1206                         for (int i = 0;
1207                              i < MAX_TRIGGER_SEQUENCE_FRAMES && !precaptureComplete;
1208                              i++) {
1209 
1210                             precaptureComplete = verifyAeSequence(aeState, precaptureComplete);
1211 
1212                             CaptureResult precaptureResult = captureListener.getCaptureResult(
1213                                 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
1214                             aeState = precaptureResult.get(CaptureResult.CONTROL_AE_STATE);
1215                         }
1216 
1217                         assertTrue("Precapture sequence never completed!", precaptureComplete);
1218 
1219                         for (int i = 0; i < MAX_RESULT_STATE_POSTCHANGE_WAIT_FRAMES; i++) {
1220                             CaptureResult postPrecaptureResult = captureListener.getCaptureResult(
1221                                 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
1222                             aeState = postPrecaptureResult.get(CaptureResult.CONTROL_AE_STATE);
1223                             assertTrue("Late transition to PRECAPTURE state seen",
1224                                     aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE);
1225                         }
1226 
1227                         // Done
1228 
1229                         stopCapture(/*fast*/ false);
1230                         preview.release();
1231                     }
1232 
1233                 }
1234 
1235             } finally {
1236                 closeDevice(id);
1237             }
1238         }
1239 
1240     }
1241 
1242     @Test
testSimultaneousTriggers()1243     public void testSimultaneousTriggers() throws Exception {
1244         for (String id : getCameraIdsUnderTest()) {
1245             Log.i(TAG, String.format("Testing Camera %s", id));
1246 
1247             try {
1248                 // Legacy devices do not support precapture trigger; don't test devices that
1249                 // can't focus
1250                 StaticMetadata staticInfo = mAllStaticInfo.get(id);
1251                 if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) {
1252                     continue;
1253                 }
1254                 // Depth-only devices won't support AE
1255                 if (!staticInfo.isColorOutputSupported()) {
1256                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
1257                     continue;
1258                 }
1259 
1260                 openDevice(id);
1261                 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
1262                 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
1263 
1264                 for (int afMode : availableAfModes) {
1265                     if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
1266                             afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
1267                         // Only test AF modes that have meaningful trigger behavior
1268                         continue;
1269                     }
1270 
1271                     for (int aeMode : availableAeModes) {
1272                         if (aeMode ==  CameraCharacteristics.CONTROL_AE_MODE_OFF) {
1273                             // Only test AE modes that have meaningful trigger behavior
1274                             continue;
1275                         }
1276 
1277                         SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
1278 
1279                         CaptureRequest.Builder previewRequest =
1280                                 prepareTriggerTestSession(preview, aeMode, afMode);
1281 
1282                         SimpleCaptureCallback captureListener =
1283                                 new CameraTestUtils.SimpleCaptureCallback();
1284 
1285                         mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener,
1286                                 mHandler);
1287 
1288                         // Cancel triggers
1289 
1290                         cancelTriggersAndWait(previewRequest, captureListener, afMode);
1291 
1292                         //
1293                         // Trigger AF and AE together
1294 
1295                         if (VERBOSE) {
1296                             Log.v(TAG, String.format("Triggering AF and AE together"));
1297                         }
1298 
1299                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
1300                                 CaptureRequest.CONTROL_AF_TRIGGER_START);
1301                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
1302                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
1303 
1304                         CaptureRequest triggerRequest = previewRequest.build();
1305                         mCameraSession.capture(triggerRequest, captureListener, mHandler);
1306 
1307                         CaptureResult triggerResult = captureListener.getCaptureResultForRequest(
1308                                 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
1309                         int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE);
1310                         int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
1311 
1312                         boolean precaptureComplete = false;
1313                         boolean focusComplete = false;
1314 
1315                         for (int i = 0;
1316                              i < MAX_TRIGGER_SEQUENCE_FRAMES &&
1317                                      !(focusComplete && precaptureComplete);
1318                              i++) {
1319 
1320                             focusComplete = verifyAfSequence(afMode, afState, focusComplete);
1321                             precaptureComplete = verifyAeSequence(aeState, precaptureComplete);
1322 
1323                             CaptureResult sequenceResult = captureListener.getCaptureResult(
1324                                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
1325                             afState = sequenceResult.get(CaptureResult.CONTROL_AF_STATE);
1326                             aeState = sequenceResult.get(CaptureResult.CONTROL_AE_STATE);
1327                         }
1328 
1329                         assertTrue("Precapture sequence never completed!", precaptureComplete);
1330                         assertTrue("Focus sequence never completed!", focusComplete);
1331 
1332                         // Done
1333 
1334                         stopCapture(/*fast*/ false);
1335                         preview.release();
1336 
1337                     }
1338                 }
1339             } finally {
1340                 closeDevice(id);
1341             }
1342         }
1343     }
1344 
1345     @Test
testAfThenAeTrigger()1346     public void testAfThenAeTrigger() throws Exception {
1347         for (String id : getCameraIdsUnderTest()) {
1348             Log.i(TAG, String.format("Testing Camera %s", id));
1349 
1350             try {
1351                 // Legacy devices do not support precapture trigger; don't test devices that
1352                 // can't focus
1353                 StaticMetadata staticInfo = mAllStaticInfo.get(id);
1354                 if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) {
1355                     continue;
1356                 }
1357                 // Depth-only devices won't support AE
1358                 if (!staticInfo.isColorOutputSupported()) {
1359                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
1360                     continue;
1361                 }
1362 
1363                 openDevice(id);
1364                 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
1365                 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
1366 
1367                 for (int afMode : availableAfModes) {
1368                     if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
1369                             afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
1370                         // Only test AF modes that have meaningful trigger behavior
1371                         continue;
1372                     }
1373 
1374                     for (int aeMode : availableAeModes) {
1375                         if (aeMode ==  CameraCharacteristics.CONTROL_AE_MODE_OFF) {
1376                             // Only test AE modes that have meaningful trigger behavior
1377                             continue;
1378                         }
1379 
1380                         SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
1381 
1382                         CaptureRequest.Builder previewRequest =
1383                                 prepareTriggerTestSession(preview, aeMode, afMode);
1384 
1385                         SimpleCaptureCallback captureListener =
1386                                 new CameraTestUtils.SimpleCaptureCallback();
1387 
1388                         mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener,
1389                                 mHandler);
1390 
1391                         // Cancel triggers
1392 
1393                         cancelTriggersAndWait(previewRequest, captureListener, afMode);
1394 
1395                         //
1396                         // AF with AE a request later
1397 
1398                         if (VERBOSE) {
1399                             Log.v(TAG, "Trigger AF, then AE trigger on next request");
1400                         }
1401 
1402                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
1403                                 CaptureRequest.CONTROL_AF_TRIGGER_START);
1404                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
1405                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
1406 
1407                         CaptureRequest triggerRequest = previewRequest.build();
1408                         mCameraSession.capture(triggerRequest, captureListener, mHandler);
1409 
1410                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
1411                                 CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
1412                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
1413                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
1414 
1415                         CaptureRequest triggerRequest2 = previewRequest.build();
1416                         mCameraSession.capture(triggerRequest2, captureListener, mHandler);
1417 
1418                         CaptureResult triggerResult = captureListener.getCaptureResultForRequest(
1419                                 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
1420                         int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
1421 
1422                         boolean precaptureComplete = false;
1423                         boolean focusComplete = false;
1424 
1425                         focusComplete = verifyAfSequence(afMode, afState, focusComplete);
1426 
1427                         triggerResult = captureListener.getCaptureResultForRequest(
1428                                 triggerRequest2, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
1429                         afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
1430                         int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE);
1431 
1432                         for (int i = 0;
1433                              i < MAX_TRIGGER_SEQUENCE_FRAMES &&
1434                                      !(focusComplete && precaptureComplete);
1435                              i++) {
1436 
1437                             focusComplete = verifyAfSequence(afMode, afState, focusComplete);
1438                             precaptureComplete = verifyAeSequence(aeState, precaptureComplete);
1439 
1440                             CaptureResult sequenceResult = captureListener.getCaptureResult(
1441                                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
1442                             afState = sequenceResult.get(CaptureResult.CONTROL_AF_STATE);
1443                             aeState = sequenceResult.get(CaptureResult.CONTROL_AE_STATE);
1444                         }
1445 
1446                         assertTrue("Precapture sequence never completed!", precaptureComplete);
1447                         assertTrue("Focus sequence never completed!", focusComplete);
1448 
1449                         // Done
1450 
1451                         stopCapture(/*fast*/ false);
1452                         preview.release();
1453 
1454                     }
1455                 }
1456             } finally {
1457                 closeDevice(id);
1458             }
1459         }
1460     }
1461 
1462     @Test
testAeThenAfTrigger()1463     public void testAeThenAfTrigger() throws Exception {
1464         for (String id : getCameraIdsUnderTest()) {
1465             Log.i(TAG, String.format("Testing Camera %s", id));
1466 
1467             try {
1468                 // Legacy devices do not support precapture trigger; don't test devices that
1469                 // can't focus
1470                 StaticMetadata staticInfo = mAllStaticInfo.get(id);
1471                 if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) {
1472                     continue;
1473                 }
1474                 // Depth-only devices won't support AE
1475                 if (!staticInfo.isColorOutputSupported()) {
1476                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
1477                     continue;
1478                 }
1479 
1480                 openDevice(id);
1481                 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
1482                 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
1483 
1484                 for (int afMode : availableAfModes) {
1485                     if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
1486                             afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
1487                         // Only test AF modes that have meaningful trigger behavior
1488                         continue;
1489                     }
1490 
1491                     for (int aeMode : availableAeModes) {
1492                         if (aeMode ==  CameraCharacteristics.CONTROL_AE_MODE_OFF) {
1493                             // Only test AE modes that have meaningful trigger behavior
1494                             continue;
1495                         }
1496 
1497                         SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
1498 
1499                         CaptureRequest.Builder previewRequest =
1500                                 prepareTriggerTestSession(preview, aeMode, afMode);
1501 
1502                         SimpleCaptureCallback captureListener =
1503                                 new CameraTestUtils.SimpleCaptureCallback();
1504 
1505                         mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener,
1506                                 mHandler);
1507 
1508                         // Cancel triggers
1509 
1510                         cancelTriggersAndWait(previewRequest, captureListener, afMode);
1511 
1512                         //
1513                         // AE with AF a request later
1514 
1515                         if (VERBOSE) {
1516                             Log.v(TAG, "Trigger AE, then AF trigger on next request");
1517                         }
1518 
1519                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
1520                                 CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
1521                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
1522                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
1523 
1524                         CaptureRequest triggerRequest = previewRequest.build();
1525                         mCameraSession.capture(triggerRequest, captureListener, mHandler);
1526 
1527                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
1528                                 CaptureRequest.CONTROL_AF_TRIGGER_START);
1529                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
1530                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
1531 
1532                         CaptureRequest triggerRequest2 = previewRequest.build();
1533                         mCameraSession.capture(triggerRequest2, captureListener, mHandler);
1534 
1535                         CaptureResult triggerResult = captureListener.getCaptureResultForRequest(
1536                                 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
1537                         int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE);
1538 
1539                         boolean precaptureComplete = false;
1540                         boolean focusComplete = false;
1541 
1542                         precaptureComplete = verifyAeSequence(aeState, precaptureComplete);
1543 
1544                         triggerResult = captureListener.getCaptureResultForRequest(
1545                                 triggerRequest2, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
1546                         int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
1547                         aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE);
1548 
1549                         for (int i = 0;
1550                              i < MAX_TRIGGER_SEQUENCE_FRAMES &&
1551                                      !(focusComplete && precaptureComplete);
1552                              i++) {
1553 
1554                             focusComplete = verifyAfSequence(afMode, afState, focusComplete);
1555                             precaptureComplete = verifyAeSequence(aeState, precaptureComplete);
1556 
1557                             CaptureResult sequenceResult = captureListener.getCaptureResult(
1558                                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
1559                             afState = sequenceResult.get(CaptureResult.CONTROL_AF_STATE);
1560                             aeState = sequenceResult.get(CaptureResult.CONTROL_AE_STATE);
1561                         }
1562 
1563                         assertTrue("Precapture sequence never completed!", precaptureComplete);
1564                         assertTrue("Focus sequence never completed!", focusComplete);
1565 
1566                         // Done
1567 
1568                         stopCapture(/*fast*/ false);
1569                         preview.release();
1570 
1571                     }
1572                 }
1573             } finally {
1574                 closeDevice(id);
1575             }
1576         }
1577     }
1578 
1579     @Test
testAeAndAfCausality()1580     public void testAeAndAfCausality() throws Exception {
1581 
1582         for (String id : getCameraIdsUnderTest()) {
1583             Log.i(TAG, String.format("Testing Camera %s", id));
1584 
1585             try {
1586                 // Legacy devices do not support precapture trigger; don't test devices that
1587                 // can't focus
1588                 StaticMetadata staticInfo = mAllStaticInfo.get(id);
1589                 if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) {
1590                     continue;
1591                 }
1592                 // Depth-only devices won't support AE
1593                 if (!staticInfo.isColorOutputSupported()) {
1594                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
1595                     continue;
1596                 }
1597 
1598                 openDevice(id);
1599                 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
1600                 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
1601                 final int maxPipelineDepth = mStaticInfo.getCharacteristics().get(
1602                         CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH);
1603 
1604                 for (int afMode : availableAfModes) {
1605                     if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
1606                             afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
1607                         // Only test AF modes that have meaningful trigger behavior
1608                         continue;
1609                     }
1610                     for (int aeMode : availableAeModes) {
1611                         if (aeMode ==  CameraCharacteristics.CONTROL_AE_MODE_OFF) {
1612                             // Only test AE modes that have meaningful trigger behavior
1613                             continue;
1614                         }
1615 
1616                         SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
1617 
1618                         CaptureRequest.Builder previewRequest =
1619                                 prepareTriggerTestSession(preview, aeMode, afMode);
1620 
1621                         SimpleCaptureCallback captureListener =
1622                                 new CameraTestUtils.SimpleCaptureCallback();
1623 
1624                         mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener,
1625                                 mHandler);
1626 
1627                         List<CaptureRequest> triggerRequests =
1628                                 new ArrayList<CaptureRequest>(maxPipelineDepth+1);
1629                         for (int i = 0; i < maxPipelineDepth; i++) {
1630                             triggerRequests.add(previewRequest.build());
1631                         }
1632 
1633                         // Cancel triggers
1634                         cancelTriggersAndWait(previewRequest, captureListener, afMode);
1635 
1636                         //
1637                         // Standard sequence - Part 1 AF trigger
1638 
1639                         if (VERBOSE) {
1640                             Log.v(TAG, String.format("Triggering AF"));
1641                         }
1642 
1643                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
1644                                 CaptureRequest.CONTROL_AF_TRIGGER_START);
1645                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
1646                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
1647                         triggerRequests.add(previewRequest.build());
1648 
1649                         mCameraSession.captureBurst(triggerRequests, captureListener, mHandler);
1650 
1651                         TotalCaptureResult[] triggerResults =
1652                                 captureListener.getTotalCaptureResultsForRequests(
1653                                 triggerRequests, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
1654                         for (int i = 0; i < maxPipelineDepth; i++) {
1655                             TotalCaptureResult triggerResult = triggerResults[i];
1656                             int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
1657                             int afTrigger = triggerResult.get(CaptureResult.CONTROL_AF_TRIGGER);
1658 
1659                             verifyStartingAfState(afMode, afState);
1660                             assertTrue(String.format("In AF mode %s, previous AF_TRIGGER must not "
1661                                     + "be START before TRIGGER_START",
1662                                     StaticMetadata.getAfModeName(afMode)),
1663                                     afTrigger != CaptureResult.CONTROL_AF_TRIGGER_START);
1664                         }
1665 
1666                         int afState =
1667                                 triggerResults[maxPipelineDepth].get(CaptureResult.CONTROL_AF_STATE);
1668                         boolean focusComplete = false;
1669                         for (int i = 0;
1670                              i < MAX_TRIGGER_SEQUENCE_FRAMES && !focusComplete;
1671                              i++) {
1672 
1673                             focusComplete = verifyAfSequence(afMode, afState, focusComplete);
1674 
1675                             CaptureResult focusResult = captureListener.getCaptureResult(
1676                                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
1677                             afState = focusResult.get(CaptureResult.CONTROL_AF_STATE);
1678                         }
1679 
1680                         assertTrue("Focusing never completed!", focusComplete);
1681 
1682                         // Standard sequence - Part 2 AE trigger
1683 
1684                         if (VERBOSE) {
1685                             Log.v(TAG, String.format("Triggering AE"));
1686                         }
1687                         // Remove AF trigger request
1688                         triggerRequests.remove(maxPipelineDepth);
1689 
1690                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
1691                                 CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
1692                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
1693                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
1694                         triggerRequests.add(previewRequest.build());
1695 
1696                         mCameraSession.captureBurst(triggerRequests, captureListener, mHandler);
1697 
1698                         triggerResults = captureListener.getTotalCaptureResultsForRequests(
1699                                 triggerRequests, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
1700 
1701                         for (int i = 0; i < maxPipelineDepth; i++) {
1702                             TotalCaptureResult triggerResult = triggerResults[i];
1703                             int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE);
1704                             int aeTrigger = triggerResult.get(
1705                                     CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER);
1706 
1707                             assertTrue(String.format("In AE mode %s, previous AE_TRIGGER must not "
1708                                     + "be START before TRIGGER_START",
1709                                     StaticMetadata.getAeModeName(aeMode)),
1710                                     aeTrigger != CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER_START);
1711                             assertTrue(String.format("In AE mode %s, previous AE_STATE must not be"
1712                                     + " PRECAPTURE_TRIGGER before TRIGGER_START",
1713                                     StaticMetadata.getAeModeName(aeMode)),
1714                                     aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE);
1715                         }
1716 
1717                         // Stand sequence - Part 3 Cancel AF trigger
1718                         if (VERBOSE) {
1719                             Log.v(TAG, String.format("Cancel AF trigger"));
1720                         }
1721                         // Remove AE trigger request
1722                         triggerRequests.remove(maxPipelineDepth);
1723                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
1724                                 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
1725                         triggerRequests.add(previewRequest.build());
1726 
1727                         mCameraSession.captureBurst(triggerRequests, captureListener, mHandler);
1728                         triggerResults = captureListener.getTotalCaptureResultsForRequests(
1729                                 triggerRequests, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
1730                         for (int i = 0; i < maxPipelineDepth; i++) {
1731                             TotalCaptureResult triggerResult = triggerResults[i];
1732                             afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
1733                             int afTrigger = triggerResult.get(CaptureResult.CONTROL_AF_TRIGGER);
1734 
1735                             assertTrue(
1736                                     String.format("In AF mode %s, previous AF_TRIGGER must not " +
1737                                     "be CANCEL before TRIGGER_CANCEL",
1738                                     StaticMetadata.getAfModeName(afMode)),
1739                                     afTrigger != CaptureResult.CONTROL_AF_TRIGGER_CANCEL);
1740                             assertTrue(
1741                                     String.format("In AF mode %s, previous AF_STATE must be LOCKED"
1742                                     + " before CANCEL, but is %s",
1743                                     StaticMetadata.getAfModeName(afMode),
1744                                     StaticMetadata.AF_STATE_NAMES[afState]),
1745                                     afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
1746                                     afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED);
1747                         }
1748 
1749                         stopCapture(/*fast*/ false);
1750                         preview.release();
1751                     }
1752 
1753                 }
1754 
1755             } finally {
1756                 closeDevice(id);
1757             }
1758         }
1759 
1760     }
1761 
1762     @Test
testAbandonRepeatingRequestSurface()1763     public void testAbandonRepeatingRequestSurface() throws Exception {
1764         for (String id : getCameraIdsUnderTest()) {
1765             Log.i(TAG, String.format(
1766                     "Testing Camera %s for abandoning surface of a repeating request", id));
1767 
1768             StaticMetadata staticInfo = mAllStaticInfo.get(id);
1769             if (!staticInfo.isColorOutputSupported()) {
1770                 Log.i(TAG, "Camera " + id + " does not support color output, skipping");
1771                 continue;
1772             }
1773 
1774             openDevice(id);
1775 
1776             try {
1777 
1778                 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
1779                 Surface previewSurface = new Surface(preview);
1780 
1781                 CaptureRequest.Builder previewRequest = preparePreviewTestSession(preview);
1782                 SimpleCaptureCallback captureListener = new CameraTestUtils.SimpleCaptureCallback();
1783 
1784                 int sequenceId = mCameraSession.setRepeatingRequest(previewRequest.build(),
1785                         captureListener, mHandler);
1786 
1787                 for (int i = 0; i < PREVIEW_WARMUP_FRAMES; i++) {
1788                     captureListener.getTotalCaptureResult(CAPTURE_TIMEOUT);
1789                 }
1790 
1791                 // Abandon preview surface.
1792                 preview.release();
1793 
1794                 // Check onCaptureSequenceCompleted is received.
1795                 long sequenceLastFrameNumber = captureListener.getCaptureSequenceLastFrameNumber(
1796                         sequenceId, CAPTURE_TIMEOUT);
1797 
1798                 mCameraSession.stopRepeating();
1799 
1800                 // Find the last frame number received in results and failures.
1801                 long lastFrameNumber = -1;
1802                 while (captureListener.hasMoreResults()) {
1803                     TotalCaptureResult result = captureListener.getTotalCaptureResult(
1804                             CAPTURE_TIMEOUT);
1805                     if (lastFrameNumber < result.getFrameNumber()) {
1806                         lastFrameNumber = result.getFrameNumber();
1807                     }
1808                 }
1809 
1810                 while (captureListener.hasMoreFailures()) {
1811                     ArrayList<CaptureFailure> failures = captureListener.getCaptureFailures(
1812                             /*maxNumFailures*/ 1);
1813                     for (CaptureFailure failure : failures) {
1814                         if (lastFrameNumber < failure.getFrameNumber()) {
1815                             lastFrameNumber = failure.getFrameNumber();
1816                         }
1817                     }
1818                 }
1819 
1820                 // Verify the last frame number received from capture sequence completed matches the
1821                 // the last frame number of the results and failures.
1822                 assertEquals(String.format("Last frame number from onCaptureSequenceCompleted " +
1823                         "(%d) doesn't match the last frame number received from " +
1824                         "results/failures (%d)", sequenceLastFrameNumber, lastFrameNumber),
1825                         sequenceLastFrameNumber, lastFrameNumber);
1826             } finally {
1827                 closeDevice(id);
1828             }
1829         }
1830     }
1831 
1832     @Test
testConfigureInvalidSensorPixelModes()1833     public void testConfigureInvalidSensorPixelModes() throws Exception {
1834         for (String id : getCameraIdsUnderTest()) {
1835             // Go through given, stream configuration map, add the incorrect sensor pixel mode
1836             // to an OutputConfiguration, make sure the session configuration fails.
1837             CameraCharacteristics chars = mCameraManager.getCameraCharacteristics(id);
1838             StreamConfigurationMap defaultStreamConfigMap =
1839                     chars.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1840             StreamConfigurationMap maxStreamConfigMap =
1841                     chars.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION);
1842             openDevice(id);
1843             try {
1844                 verifyBasicSensorPixelModes(id, maxStreamConfigMap, defaultStreamConfigMap,
1845                         /*maxResolution*/ false);
1846                 verifyBasicSensorPixelModes(id, maxStreamConfigMap, defaultStreamConfigMap,
1847                         /*maxResolution*/ true);
1848             } finally {
1849                 closeDevice(id);
1850             }
1851         }
1852     }
1853 
1854     @Test
testConfigureAbandonedSurface()1855     public void testConfigureAbandonedSurface() throws Exception {
1856         for (String id : getCameraIdsUnderTest()) {
1857             Log.i(TAG, String.format(
1858                     "Testing Camera %s for configuring abandoned surface", id));
1859 
1860             openDevice(id);
1861             try {
1862                 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
1863                 Surface previewSurface = new Surface(preview);
1864 
1865                 // Abandon preview SurfaceTexture.
1866                 preview.release();
1867 
1868                 try {
1869                     CaptureRequest.Builder previewRequest = preparePreviewTestSession(preview);
1870                     fail("Configuring abandoned surfaces must fail!");
1871                 } catch (IllegalArgumentException e) {
1872                     // expected
1873                     Log.i(TAG, "normal session check passed");
1874                 }
1875 
1876                 // Try constrained high speed session/requests
1877                 if (!mStaticInfo.isConstrainedHighSpeedVideoSupported()) {
1878                     continue;
1879                 }
1880 
1881                 List<Surface> surfaces = new ArrayList<>();
1882                 surfaces.add(previewSurface);
1883                 CameraCaptureSession.StateCallback sessionListener =
1884                         mock(CameraCaptureSession.StateCallback.class);
1885 
1886                 try {
1887                     mCamera.createConstrainedHighSpeedCaptureSession(surfaces,
1888                             sessionListener, mHandler);
1889                     fail("Configuring abandoned surfaces in high speed session must fail!");
1890                 } catch (IllegalArgumentException e) {
1891                     // expected
1892                     Log.i(TAG, "high speed session check 1 passed");
1893                 }
1894 
1895                 // Also try abandone the Surface directly
1896                 previewSurface.release();
1897 
1898                 try {
1899                     mCamera.createConstrainedHighSpeedCaptureSession(surfaces,
1900                             sessionListener, mHandler);
1901                     fail("Configuring abandoned surfaces in high speed session must fail!");
1902                 } catch (IllegalArgumentException e) {
1903                     // expected
1904                     Log.i(TAG, "high speed session check 2 passed");
1905                 }
1906             } finally {
1907                 closeDevice(id);
1908             }
1909         }
1910     }
1911 
1912     @Test
testAfSceneChange()1913     public void testAfSceneChange() throws Exception {
1914         final int NUM_FRAMES_VERIFIED = 3;
1915 
1916         for (String id : getCameraIdsUnderTest()) {
1917             Log.i(TAG, String.format("Testing Camera %s for AF scene change", id));
1918 
1919             StaticMetadata staticInfo =
1920                     new StaticMetadata(mCameraManager.getCameraCharacteristics(id));
1921             if (!staticInfo.isAfSceneChangeSupported()) {
1922                 continue;
1923             }
1924 
1925             openDevice(id);
1926 
1927             try {
1928                 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
1929                 Surface previewSurface = new Surface(preview);
1930 
1931                 CaptureRequest.Builder previewRequest = preparePreviewTestSession(preview);
1932                 SimpleCaptureCallback previewListener = new CameraTestUtils.SimpleCaptureCallback();
1933 
1934                 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
1935 
1936                 // Test AF scene change in each AF mode.
1937                 for (int afMode : availableAfModes) {
1938                     previewRequest.set(CaptureRequest.CONTROL_AF_MODE, afMode);
1939 
1940                     int sequenceId = mCameraSession.setRepeatingRequest(previewRequest.build(),
1941                             previewListener, mHandler);
1942 
1943                     // Verify that AF scene change is NOT_DETECTED or DETECTED.
1944                     for (int i = 0; i < NUM_FRAMES_VERIFIED; i++) {
1945                         TotalCaptureResult result =
1946                             previewListener.getTotalCaptureResult(CAPTURE_TIMEOUT);
1947                         mCollector.expectKeyValueIsIn(result,
1948                                 CaptureResult.CONTROL_AF_SCENE_CHANGE,
1949                                 CaptureResult.CONTROL_AF_SCENE_CHANGE_DETECTED,
1950                                 CaptureResult.CONTROL_AF_SCENE_CHANGE_NOT_DETECTED);
1951                     }
1952 
1953                     mCameraSession.stopRepeating();
1954                     previewListener.getCaptureSequenceLastFrameNumber(sequenceId, CAPTURE_TIMEOUT);
1955                     previewListener.drain();
1956                 }
1957             } finally {
1958                 closeDevice(id);
1959             }
1960         }
1961     }
1962 
1963     @Test
testOisDataMode()1964     public void testOisDataMode() throws Exception {
1965         final int NUM_FRAMES_VERIFIED = 3;
1966 
1967         for (String id : getCameraIdsUnderTest()) {
1968             Log.i(TAG, String.format("Testing Camera %s for OIS mode", id));
1969 
1970             StaticMetadata staticInfo =
1971                     new StaticMetadata(mCameraManager.getCameraCharacteristics(id));
1972             if (!staticInfo.isOisDataModeSupported()) {
1973                 continue;
1974             }
1975 
1976             openDevice(id);
1977 
1978             try {
1979                 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
1980                 Surface previewSurface = new Surface(preview);
1981 
1982                 CaptureRequest.Builder previewRequest = preparePreviewTestSession(preview);
1983                 SimpleCaptureCallback previewListener = new CameraTestUtils.SimpleCaptureCallback();
1984 
1985                 int[] availableOisDataModes = staticInfo.getCharacteristics().get(
1986                         CameraCharacteristics.STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES);
1987 
1988                 // Test each OIS data mode
1989                 for (int oisMode : availableOisDataModes) {
1990                     previewRequest.set(CaptureRequest.STATISTICS_OIS_DATA_MODE, oisMode);
1991 
1992                     int sequenceId = mCameraSession.setRepeatingRequest(previewRequest.build(),
1993                             previewListener, mHandler);
1994 
1995                     // Check OIS data in each mode.
1996                     for (int i = 0; i < NUM_FRAMES_VERIFIED; i++) {
1997                         TotalCaptureResult result =
1998                             previewListener.getTotalCaptureResult(CAPTURE_TIMEOUT);
1999 
2000                         OisSample[] oisSamples = result.get(CaptureResult.STATISTICS_OIS_SAMPLES);
2001 
2002                         boolean physicalDeviceSupportsOIS = true;
2003                         if (staticInfo.isLogicalMultiCamera() &&
2004                                 staticInfo.isActivePhysicalCameraIdSupported()) {
2005                             String physicalId = result.get(
2006                                     CaptureResult.LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID);
2007                             assertNotNull(physicalId);
2008                             StaticMetadata physicalStaticInfo = mAllStaticInfo.get(physicalId);
2009                             physicalDeviceSupportsOIS = physicalStaticInfo.isOisDataModeSupported();
2010                         }
2011 
2012                         if (oisMode == CameraCharacteristics.STATISTICS_OIS_DATA_MODE_OFF ||
2013                                 !physicalDeviceSupportsOIS) {
2014                             mCollector.expectKeyValueEquals(result,
2015                                     CaptureResult.STATISTICS_OIS_DATA_MODE,
2016                                     CaptureResult.STATISTICS_OIS_DATA_MODE_OFF);
2017                             mCollector.expectTrue("OIS samples reported in OIS_DATA_MODE_OFF",
2018                                     oisSamples == null || oisSamples.length == 0);
2019 
2020                         } else if (oisMode == CameraCharacteristics.STATISTICS_OIS_DATA_MODE_ON) {
2021                             mCollector.expectKeyValueEquals(result,
2022                                     CaptureResult.STATISTICS_OIS_DATA_MODE,
2023                                     CaptureResult.STATISTICS_OIS_DATA_MODE_ON);
2024                             mCollector.expectTrue("OIS samples not reported in OIS_DATA_MODE_ON",
2025                                     oisSamples != null && oisSamples.length != 0);
2026                         } else {
2027                             mCollector.addMessage(String.format("Invalid OIS mode: %d", oisMode));
2028                         }
2029                     }
2030 
2031                     mCameraSession.stopRepeating();
2032                     previewListener.getCaptureSequenceLastFrameNumber(sequenceId, CAPTURE_TIMEOUT);
2033                     previewListener.drain();
2034                 }
2035             } finally {
2036                 closeDevice(id);
2037             }
2038         }
2039     }
2040 
preparePreviewTestSession(SurfaceTexture preview)2041     private CaptureRequest.Builder preparePreviewTestSession(SurfaceTexture preview)
2042             throws Exception {
2043         Surface previewSurface = new Surface(preview);
2044 
2045         preview.setDefaultBufferSize(640, 480);
2046 
2047         ArrayList<Surface> sessionOutputs = new ArrayList<>();
2048         sessionOutputs.add(previewSurface);
2049 
2050         createSession(sessionOutputs);
2051 
2052         CaptureRequest.Builder previewRequest =
2053                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
2054 
2055         previewRequest.addTarget(previewSurface);
2056 
2057         return previewRequest;
2058     }
2059 
prepareTriggerTestSession( SurfaceTexture preview, int aeMode, int afMode)2060     private CaptureRequest.Builder prepareTriggerTestSession(
2061             SurfaceTexture preview, int aeMode, int afMode) throws Exception {
2062         Log.i(TAG, String.format("Testing AE mode %s, AF mode %s",
2063                         StaticMetadata.getAeModeName(aeMode),
2064                         StaticMetadata.getAfModeName(afMode)));
2065 
2066         CaptureRequest.Builder previewRequest = preparePreviewTestSession(preview);
2067         previewRequest.set(CaptureRequest.CONTROL_AE_MODE, aeMode);
2068         previewRequest.set(CaptureRequest.CONTROL_AF_MODE, afMode);
2069 
2070         return previewRequest;
2071     }
2072 
cancelTriggersAndWait(CaptureRequest.Builder previewRequest, SimpleCaptureCallback captureListener, int afMode)2073     private void cancelTriggersAndWait(CaptureRequest.Builder previewRequest,
2074             SimpleCaptureCallback captureListener, int afMode) throws Exception {
2075         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
2076                 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
2077         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
2078                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL);
2079 
2080         CaptureRequest triggerRequest = previewRequest.build();
2081         mCameraSession.capture(triggerRequest, captureListener, mHandler);
2082 
2083         // Wait for a few frames to initialize 3A
2084 
2085         CaptureResult previewResult = null;
2086         int afState;
2087         int aeState;
2088 
2089         for (int i = 0; i < PREVIEW_WARMUP_FRAMES; i++) {
2090             previewResult = captureListener.getCaptureResult(
2091                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
2092             if (VERBOSE) {
2093                 afState = previewResult.get(CaptureResult.CONTROL_AF_STATE);
2094                 aeState = previewResult.get(CaptureResult.CONTROL_AE_STATE);
2095                 Log.v(TAG, String.format("AF state: %s, AE state: %s",
2096                                 StaticMetadata.AF_STATE_NAMES[afState],
2097                                 StaticMetadata.AE_STATE_NAMES[aeState]));
2098             }
2099         }
2100 
2101         // Verify starting states
2102 
2103         afState = previewResult.get(CaptureResult.CONTROL_AF_STATE);
2104         aeState = previewResult.get(CaptureResult.CONTROL_AE_STATE);
2105 
2106         verifyStartingAfState(afMode, afState);
2107 
2108         // After several frames, AE must no longer be in INACTIVE state
2109         assertTrue(String.format("AE state must be SEARCHING, CONVERGED, " +
2110                         "or FLASH_REQUIRED, is %s", StaticMetadata.AE_STATE_NAMES[aeState]),
2111                 aeState == CaptureResult.CONTROL_AE_STATE_SEARCHING ||
2112                 aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED ||
2113                 aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED);
2114     }
2115 
configsContain(StreamConfigurationMap configs, int format, Size size)2116     private boolean configsContain(StreamConfigurationMap configs, int format, Size size) {
2117         Size[] sizes = configs.getOutputSizes(format);
2118         if (sizes == null) {
2119             return false;
2120         }
2121         return Arrays.asList(sizes).contains(size);
2122     }
2123 
verifyBasicSensorPixelModes(String id, StreamConfigurationMap maxResConfigs, StreamConfigurationMap defaultConfigs, boolean maxResolution)2124     private void verifyBasicSensorPixelModes(String id, StreamConfigurationMap maxResConfigs,
2125             StreamConfigurationMap defaultConfigs, boolean maxResolution) throws Exception {
2126         // Go through StreamConfiguration map, set up OutputConfiguration and add the opposite
2127         // sensorPixelMode.
2128         final int MIN_RESULT_COUNT = 3;
2129         assertTrue("Default stream config map must be present for id: " + id,
2130             defaultConfigs != null);
2131         if (maxResConfigs == null) {
2132             Log.i(TAG, "camera id " + id + " has no StreamConfigurationMap for max resolution " +
2133                 ", skipping verifyBasicSensorPixelModes");
2134             return;
2135         }
2136         StreamConfigurationMap chosenConfigs = maxResolution ? maxResConfigs : defaultConfigs;
2137         StreamConfigurationMap otherConfigs = maxResolution ? defaultConfigs : maxResConfigs;
2138         OutputConfiguration outputConfig = null;
2139         for (int format : chosenConfigs.getOutputFormats()) {
2140             Size targetSize = CameraTestUtils.getMaxSize(chosenConfigs.getOutputSizes(format));
2141             if (configsContain(otherConfigs, format, targetSize)) {
2142                 // Since both max res and default stream configuration maps contain this size,
2143                 // both sensor pixel modes are valid.
2144                 Log.v(TAG, "camera id " + id + " 'other' configs with maxResolution" +
2145                     maxResolution + " contains the format: " + format + " size: " + targetSize +
2146                     " skipping");
2147                 continue;
2148             }
2149             // Create outputConfiguration with this size and format
2150             SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
2151             SurfaceTexture textureTarget = null;
2152             ImageReader readerTarget = null;
2153             if (format == ImageFormat.PRIVATE) {
2154                 textureTarget = new SurfaceTexture(1);
2155                 textureTarget.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight());
2156                 outputConfig = new OutputConfiguration(new Surface(textureTarget));
2157             } else {
2158                 readerTarget = ImageReader.newInstance(targetSize.getWidth(),
2159                         targetSize.getHeight(), format, MIN_RESULT_COUNT);
2160                 readerTarget.setOnImageAvailableListener(imageListener, mHandler);
2161                 outputConfig = new OutputConfiguration(readerTarget.getSurface());
2162             }
2163             try {
2164                 int invalidSensorPixelMode =
2165                         maxResolution ? CameraMetadata.SENSOR_PIXEL_MODE_DEFAULT :
2166                                 CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION;
2167 
2168                 outputConfig.addSensorPixelModeUsed(invalidSensorPixelMode);
2169                 CameraCaptureSession.StateCallback sessionListener =
2170                         mock(CameraCaptureSession.StateCallback.class);
2171                 List<OutputConfiguration> outputs = new ArrayList<>();
2172                 outputs.add(outputConfig);
2173                 CameraCaptureSession session =
2174                         CameraTestUtils.configureCameraSessionWithConfig(mCamera, outputs,
2175                                 sessionListener, mHandler);
2176                 String desc = "verifyBasicSensorPixelModes : Format : " + format + " size: " +
2177                         targetSize.toString() + " maxResolution : " + maxResolution;
2178                 verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce().description(desc)).
2179                         onConfigureFailed(any(CameraCaptureSession.class));
2180                 verify(sessionListener, never().description(desc)).
2181                         onConfigured(any(CameraCaptureSession.class));
2182 
2183                 // Remove the invalid sensor pixel mode, session configuration should succeed
2184                 sessionListener = mock(CameraCaptureSession.StateCallback.class);
2185                 outputConfig.removeSensorPixelModeUsed(invalidSensorPixelMode);
2186                 CameraTestUtils.configureCameraSessionWithConfig(mCamera, outputs,
2187                         sessionListener, mHandler);
2188                 verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce().description(desc)).
2189                         onConfigured(any(CameraCaptureSession.class));
2190                 verify(sessionListener, never().description(desc)).
2191                         onConfigureFailed(any(CameraCaptureSession.class));
2192             } finally {
2193                 if (textureTarget != null) {
2194                     textureTarget.release();
2195                 }
2196 
2197                 if (readerTarget != null) {
2198                     readerTarget.close();
2199                 }
2200             }
2201         }
2202     }
2203 
verifyStartingAfState(int afMode, int afState)2204     private void verifyStartingAfState(int afMode, int afState) {
2205         switch (afMode) {
2206             case CaptureResult.CONTROL_AF_MODE_AUTO:
2207             case CaptureResult.CONTROL_AF_MODE_MACRO:
2208                 assertTrue(String.format("AF state not INACTIVE, is %s",
2209                                 StaticMetadata.AF_STATE_NAMES[afState]),
2210                         afState == CaptureResult.CONTROL_AF_STATE_INACTIVE);
2211                 break;
2212             case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE:
2213             case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_VIDEO:
2214                 // After several frames, AF must no longer be in INACTIVE state
2215                 assertTrue(String.format("In AF mode %s, AF state not PASSIVE_SCAN" +
2216                                 ", PASSIVE_FOCUSED, or PASSIVE_UNFOCUSED, is %s",
2217                                 StaticMetadata.getAfModeName(afMode),
2218                                 StaticMetadata.AF_STATE_NAMES[afState]),
2219                         afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN ||
2220                         afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED ||
2221                         afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED);
2222                 break;
2223             default:
2224                 fail("unexpected af mode");
2225         }
2226     }
2227 
verifyAfSequence(int afMode, int afState, boolean focusComplete)2228     private boolean verifyAfSequence(int afMode, int afState, boolean focusComplete) {
2229         if (focusComplete) {
2230             assertTrue(String.format("AF Mode %s: Focus lock lost after convergence: AF state: %s",
2231                             StaticMetadata.getAfModeName(afMode),
2232                             StaticMetadata.AF_STATE_NAMES[afState]),
2233                     afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
2234                     afState ==CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED);
2235             return focusComplete;
2236         }
2237         if (VERBOSE) {
2238             Log.v(TAG, String.format("AF mode: %s, AF state: %s",
2239                             StaticMetadata.getAfModeName(afMode),
2240                             StaticMetadata.AF_STATE_NAMES[afState]));
2241         }
2242         switch (afMode) {
2243             case CaptureResult.CONTROL_AF_MODE_AUTO:
2244             case CaptureResult.CONTROL_AF_MODE_MACRO:
2245                 assertTrue(String.format("AF mode %s: Unexpected AF state %s",
2246                                 StaticMetadata.getAfModeName(afMode),
2247                                 StaticMetadata.AF_STATE_NAMES[afState]),
2248                         afState == CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN ||
2249                         afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
2250                         afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED);
2251                 focusComplete =
2252                         (afState != CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN);
2253                 break;
2254             case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE:
2255                 assertTrue(String.format("AF mode %s: Unexpected AF state %s",
2256                                 StaticMetadata.getAfModeName(afMode),
2257                                 StaticMetadata.AF_STATE_NAMES[afState]),
2258                         afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN ||
2259                         afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
2260                         afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED);
2261                 focusComplete =
2262                         (afState != CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN);
2263                 break;
2264             case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_VIDEO:
2265                 assertTrue(String.format("AF mode %s: Unexpected AF state %s",
2266                                 StaticMetadata.getAfModeName(afMode),
2267                                 StaticMetadata.AF_STATE_NAMES[afState]),
2268                         afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
2269                         afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED);
2270                 focusComplete = true;
2271                 break;
2272             default:
2273                 fail("Unexpected AF mode: " + StaticMetadata.getAfModeName(afMode));
2274         }
2275         return focusComplete;
2276     }
2277 
verifyAeSequence(int aeState, boolean precaptureComplete)2278     private boolean verifyAeSequence(int aeState, boolean precaptureComplete) {
2279         if (precaptureComplete) {
2280             assertTrue("Precapture state seen after convergence",
2281                     aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE);
2282             return precaptureComplete;
2283         }
2284         if (VERBOSE) {
2285             Log.v(TAG, String.format("AE state: %s", StaticMetadata.AE_STATE_NAMES[aeState]));
2286         }
2287         switch (aeState) {
2288             case CaptureResult.CONTROL_AE_STATE_PRECAPTURE:
2289                 // scan still continuing
2290                 break;
2291             case CaptureResult.CONTROL_AE_STATE_CONVERGED:
2292             case CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED:
2293                 // completed
2294                 precaptureComplete = true;
2295                 break;
2296             default:
2297                 fail(String.format("Precapture sequence transitioned to "
2298                                 + "state %s incorrectly!", StaticMetadata.AE_STATE_NAMES[aeState]));
2299                 break;
2300         }
2301         return precaptureComplete;
2302     }
2303 
2304     /**
2305      * Test for making sure that all expected mandatory stream combinations are present and
2306      * advertised accordingly.
2307      */
2308     @Test
testVerifyMandatoryOutputCombinationTables()2309     public void testVerifyMandatoryOutputCombinationTables() throws Exception {
2310         final int[][] legacyCombinations = {
2311             // Simple preview, GPU video processing, or no-preview video recording
2312             {PRIV, MAXIMUM},
2313             // No-viewfinder still image capture
2314             {JPEG, MAXIMUM},
2315             // In-application video/image processing
2316             {YUV,  MAXIMUM},
2317             // Standard still imaging.
2318             {PRIV, PREVIEW,  JPEG, MAXIMUM},
2319             // In-app processing plus still capture.
2320             {YUV,  PREVIEW,  JPEG, MAXIMUM},
2321             // Standard recording.
2322             {PRIV, PREVIEW,  PRIV, PREVIEW},
2323             // Preview plus in-app processing.
2324             {PRIV, PREVIEW,  YUV,  PREVIEW},
2325             // Still capture plus in-app processing.
2326             {PRIV, PREVIEW,  YUV,  PREVIEW,  JPEG, MAXIMUM}
2327         };
2328 
2329         final int[][] limitedCombinations = {
2330             // High-resolution video recording with preview.
2331             {PRIV, PREVIEW,  PRIV, RECORD },
2332             // High-resolution in-app video processing with preview.
2333             {PRIV, PREVIEW,  YUV , RECORD },
2334             // Two-input in-app video processing.
2335             {YUV , PREVIEW,  YUV , RECORD },
2336             // High-resolution recording with video snapshot.
2337             {PRIV, PREVIEW,  PRIV, RECORD,   JPEG, RECORD  },
2338             // High-resolution in-app processing with video snapshot.
2339             {PRIV, PREVIEW,  YUV,  RECORD,   JPEG, RECORD  },
2340             // Two-input in-app processing with still capture.
2341             {YUV , PREVIEW,  YUV,  PREVIEW,  JPEG, MAXIMUM }
2342         };
2343 
2344         final int[][] burstCombinations = {
2345             // Maximum-resolution GPU processing with preview.
2346             {PRIV, PREVIEW,  PRIV, MAXIMUM },
2347             // Maximum-resolution in-app processing with preview.
2348             {PRIV, PREVIEW,  YUV,  MAXIMUM },
2349             // Maximum-resolution two-input in-app processing.
2350             {YUV,  PREVIEW,  YUV,  MAXIMUM },
2351         };
2352 
2353         final int[][] fullCombinations = {
2354             // Video recording with maximum-size video snapshot.
2355             {PRIV, PREVIEW,  PRIV, PREVIEW,  JPEG, MAXIMUM },
2356             // Standard video recording plus maximum-resolution in-app processing.
2357             {YUV,  VGA,      PRIV, PREVIEW,  YUV,  MAXIMUM },
2358             // Preview plus two-input maximum-resolution in-app processing.
2359             {YUV,  VGA,      YUV,  PREVIEW,  YUV,  MAXIMUM }
2360         };
2361 
2362         final int[][] rawCombinations = {
2363             // No-preview DNG capture.
2364             {RAW,  MAXIMUM },
2365             // Standard DNG capture.
2366             {PRIV, PREVIEW,  RAW,  MAXIMUM },
2367             // In-app processing plus DNG capture.
2368             {YUV,  PREVIEW,  RAW,  MAXIMUM },
2369             // Video recording with DNG capture.
2370             {PRIV, PREVIEW,  PRIV, PREVIEW,  RAW, MAXIMUM},
2371             // Preview with in-app processing and DNG capture.
2372             {PRIV, PREVIEW,  YUV,  PREVIEW,  RAW, MAXIMUM},
2373             // Two-input in-app processing plus DNG capture.
2374             {YUV,  PREVIEW,  YUV,  PREVIEW,  RAW, MAXIMUM},
2375             // Still capture with simultaneous JPEG and DNG.
2376             {PRIV, PREVIEW,  JPEG, MAXIMUM,  RAW, MAXIMUM},
2377             // In-app processing with simultaneous JPEG and DNG.
2378             {YUV,  PREVIEW,  JPEG, MAXIMUM,  RAW, MAXIMUM}
2379         };
2380 
2381         final int[][] level3Combinations = {
2382             // In-app viewfinder analysis with dynamic selection of output format
2383             {PRIV, PREVIEW, PRIV, VGA, YUV, MAXIMUM, RAW, MAXIMUM},
2384             // In-app viewfinder analysis with dynamic selection of output format
2385             {PRIV, PREVIEW, PRIV, VGA, JPEG, MAXIMUM, RAW, MAXIMUM}
2386         };
2387 
2388         final int[][] concurrentStreamCombinations = {
2389             //In-app video / image processing.
2390             {YUV, S1440P_4_3},
2391             // In-app viewfinder analysis.
2392             {PRIV, S1440P_4_3},
2393             // No viewfinder still image capture.
2394             {JPEG, S1440P_4_3},
2395             // Standard still imaging.
2396             {YUV, S720P, JPEG, S1440P_4_3},
2397             {PRIV, S720P, JPEG, S1440P_4_3},
2398             // In-app video / processing with preview.
2399             {YUV, S720P, YUV, S1440P_4_3},
2400             {YUV, S720P, PRIV, S1440P_4_3},
2401             {PRIV, S720P, YUV, S1440P_4_3},
2402             {PRIV, S720P, PRIV, S1440P_4_3}
2403         };
2404 
2405         final int[][] ultraHighResolutionsCombinations = {
2406             // Ultra high res still image capture with preview.
2407             {YUV, MAX_RES, PRIV, PREVIEW},
2408             {YUV, MAX_RES, YUV, PREVIEW},
2409             {JPEG, MAX_RES, PRIV, PREVIEW},
2410             {JPEG, MAX_RES, YUV, PREVIEW},
2411             {RAW, MAX_RES, PRIV, PREVIEW},
2412             {RAW, MAX_RES, YUV, PREVIEW},
2413             // Ultra high res still capture with preview + app based RECORD size analysis.
2414             {YUV, MAX_RES, PRIV, PREVIEW, PRIV, RECORD},
2415             {YUV, MAX_RES, PRIV, PREVIEW, YUV, RECORD},
2416             {JPEG, MAX_RES, PRIV, PREVIEW, PRIV, RECORD},
2417             {JPEG, MAX_RES, PRIV, PREVIEW, YUV, RECORD},
2418             {RAW, MAX_RES, PRIV, PREVIEW, PRIV, RECORD},
2419             {RAW, MAX_RES, PRIV, PREVIEW, YUV, RECORD},
2420             // Ultra high res still image capture with preview + default sensor pixel mode analysis
2421             // stream
2422             {YUV, MAX_RES, PRIV, PREVIEW, JPEG, MAXIMUM},
2423             {YUV, MAX_RES, PRIV, PREVIEW, YUV, MAXIMUM},
2424             {YUV, MAX_RES, PRIV, PREVIEW, RAW, MAXIMUM},
2425             {JPEG, MAX_RES, PRIV, PREVIEW, JPEG, MAXIMUM},
2426             {JPEG, MAX_RES, PRIV, PREVIEW, YUV, MAXIMUM},
2427             {JPEG, MAX_RES, PRIV, PREVIEW, RAW, MAXIMUM},
2428             {RAW, MAX_RES, PRIV, PREVIEW, JPEG, MAXIMUM},
2429             {RAW, MAX_RES, PRIV, PREVIEW, YUV, MAXIMUM},
2430             {RAW, MAX_RES, PRIV, PREVIEW, RAW, MAXIMUM},
2431         };
2432 
2433         final int[][] tenBitOutputCombinations = {
2434             // Simple preview, GPU video processing, or no-preview video recording.
2435             {PRIV, MAXIMUM},
2436             // In-application video/image processing.
2437             {YUV, MAXIMUM},
2438             // Standard still imaging.
2439             {PRIV, PREVIEW, JPEG, MAXIMUM},
2440             // Maximum-resolution in-app processing with preview.
2441             {PRIV, PREVIEW, YUV, MAXIMUM},
2442             // Maximum-resolution two-input in-app processing.
2443             {YUV, PREVIEW, YUV, MAXIMUM},
2444             // High-resolution video recording with preview.
2445             {PRIV, PREVIEW, PRIV, RECORD},
2446             // High-resolution recording with in-app snapshot.
2447             {PRIV, PREVIEW, PRIV, RECORD, YUV, RECORD},
2448             // High-resolution recording with video snapshot.
2449             {PRIV, PREVIEW, PRIV, RECORD, JPEG, RECORD}
2450         };
2451 
2452         final int[][] streamUseCaseCombinations = {
2453             // Simple preview or in-app image processing.
2454             {YUV, PREVIEW, USE_CASE_PREVIEW},
2455             {PRIV, PREVIEW, USE_CASE_PREVIEW},
2456             // Simple video recording or in-app video processing.
2457             {YUV, RECORD, USE_CASE_VIDEO_RECORD},
2458             {PRIV, RECORD, USE_CASE_VIDEO_RECORD},
2459             // Simple JPEG or YUV still image capture.
2460             {YUV, MAXIMUM, USE_CASE_STILL_CAPTURE},
2461             {JPEG, MAXIMUM, USE_CASE_STILL_CAPTURE},
2462             // Multi-purpose stream for preview, video and still image capture.
2463             {YUV, S1440P_4_3, USE_CASE_PREVIEW_VIDEO_STILL},
2464             {PRIV, S1440P_4_3, USE_CASE_PREVIEW_VIDEO_STILL},
2465             // Simple video call.
2466             {YUV, S1440P_4_3, USE_CASE_VIDEO_CALL},
2467             {PRIV, S1440P_4_3, USE_CASE_VIDEO_CALL},
2468             // Preview with JPEG or YUV still image capture.
2469             {PRIV, PREVIEW, USE_CASE_PREVIEW, YUV, MAXIMUM, USE_CASE_STILL_CAPTURE},
2470             {PRIV, PREVIEW, USE_CASE_PREVIEW, JPEG, MAXIMUM, USE_CASE_STILL_CAPTURE},
2471             // Preview with video recording or in-app video processing.
2472             {PRIV, PREVIEW, USE_CASE_PREVIEW, YUV, RECORD, USE_CASE_VIDEO_RECORD},
2473             {PRIV, PREVIEW, USE_CASE_PREVIEW, PRIV, RECORD, USE_CASE_VIDEO_RECORD},
2474             // Preview with in-application image processing.
2475             {PRIV, PREVIEW, USE_CASE_PREVIEW, YUV, PREVIEW, USE_CASE_PREVIEW},
2476             // Preview with video call.
2477             {PRIV, PREVIEW, USE_CASE_PREVIEW, YUV, S1440P_4_3, USE_CASE_VIDEO_CALL},
2478             {PRIV, PREVIEW, USE_CASE_PREVIEW, PRIV, S1440P_4_3, USE_CASE_VIDEO_CALL},
2479             // {Multi-purpose stream with JPEG or YUV still capture.
2480             {YUV, S1440P_4_3, USE_CASE_PREVIEW_VIDEO_STILL, YUV, MAXIMUM, USE_CASE_STILL_CAPTURE},
2481             {YUV, S1440P_4_3, USE_CASE_PREVIEW_VIDEO_STILL, JPEG, MAXIMUM, USE_CASE_STILL_CAPTURE},
2482             {PRIV, S1440P_4_3, USE_CASE_PREVIEW_VIDEO_STILL, YUV, MAXIMUM, USE_CASE_STILL_CAPTURE},
2483             {PRIV, S1440P_4_3, USE_CASE_PREVIEW_VIDEO_STILL, JPEG, MAXIMUM, USE_CASE_STILL_CAPTURE},
2484             // YUV and JPEG concurrent still image capture (for testing).
2485             {YUV, PREVIEW, USE_CASE_STILL_CAPTURE, JPEG, MAXIMUM, USE_CASE_STILL_CAPTURE},
2486             // Preview, video record and JPEG video snapshot.
2487             {PRIV, PREVIEW, USE_CASE_PREVIEW, YUV, RECORD, USE_CASE_VIDEO_RECORD, JPEG, RECORD,
2488                     USE_CASE_STILL_CAPTURE},
2489             {PRIV, PREVIEW, USE_CASE_PREVIEW, PRIV, RECORD, USE_CASE_VIDEO_RECORD, JPEG, RECORD,
2490                     USE_CASE_STILL_CAPTURE},
2491             // Preview, in-application image processing, and JPEG still image capture.
2492             {PRIV, PREVIEW, USE_CASE_PREVIEW, YUV, PREVIEW, USE_CASE_PREVIEW, JPEG, MAXIMUM,
2493                     USE_CASE_STILL_CAPTURE},
2494         };
2495 
2496         final int[][] streamUseCaseCroppedRawCombinations = {
2497             // Cropped RAW still image capture without preview
2498             {RAW, MAXIMUM, USE_CASE_CROPPED_RAW},
2499 
2500             // Preview / In-app processing with cropped RAW still image capture
2501             {PRIV, PREVIEW, USE_CASE_PREVIEW, RAW, MAXIMUM, USE_CASE_CROPPED_RAW},
2502             {YUV, PREVIEW, USE_CASE_PREVIEW, RAW, MAXIMUM, USE_CASE_CROPPED_RAW},
2503 
2504             // Preview / In-app processing with YUV and cropped RAW still image capture
2505             {PRIV, PREVIEW, USE_CASE_PREVIEW, YUV, MAXIMUM, USE_CASE_STILL_CAPTURE, RAW, MAXIMUM,
2506               USE_CASE_CROPPED_RAW},
2507             {YUV, PREVIEW, USE_CASE_PREVIEW, YUV, MAXIMUM, USE_CASE_STILL_CAPTURE, RAW, MAXIMUM,
2508               USE_CASE_CROPPED_RAW},
2509 
2510             // Preview / In-app processing with JPEG and cropped RAW still image capture
2511             {PRIV, PREVIEW, USE_CASE_PREVIEW, JPEG, MAXIMUM, USE_CASE_STILL_CAPTURE, RAW, MAXIMUM,
2512               USE_CASE_CROPPED_RAW},
2513             {YUV, PREVIEW, USE_CASE_PREVIEW, JPEG, MAXIMUM, USE_CASE_STILL_CAPTURE, RAW, MAXIMUM,
2514               USE_CASE_CROPPED_RAW},
2515 
2516             // Preview with in-app processing / video recording and cropped RAW snapshot
2517             {PRIV, PREVIEW, USE_CASE_PREVIEW, PRIV, PREVIEW, USE_CASE_VIDEO_RECORD, RAW, MAXIMUM,
2518               USE_CASE_CROPPED_RAW},
2519             {PRIV, PREVIEW, USE_CASE_PREVIEW, YUV, PREVIEW, USE_CASE_PREVIEW, RAW, MAXIMUM,
2520               USE_CASE_CROPPED_RAW},
2521 
2522             // Two input in-app processing with RAW
2523             {YUV, PREVIEW, USE_CASE_PREVIEW, YUV, PREVIEW, USE_CASE_PREVIEW, RAW, MAXIMUM,
2524               USE_CASE_CROPPED_RAW},
2525         };
2526 
2527 
2528         final int[][] previewStabilizationCombinations = {
2529             // Stabilized preview, GPU video processing, or no-preview stabilized video recording.
2530             {PRIV, S1440P_4_3},
2531             {YUV, S1440P_4_3},
2532             // Standard still imaging with stabilized preview.
2533             {PRIV, S1440P_4_3, JPEG, MAXIMUM},
2534             {PRIV, S1440P_4_3, YUV, MAXIMUM},
2535             {YUV, S1440P_4_3, JPEG, MAXIMUM},
2536             {YUV, S1440P_4_3, YUV, MAXIMUM},
2537             // High-resolution recording with stabilized preview and recording stream.
2538             {PRIV, PREVIEW, PRIV, S1440P_4_3},
2539             {PRIV, PREVIEW, YUV, S1440P_4_3},
2540             {YUV, PREVIEW, PRIV, S1440P_4_3},
2541             {YUV, PREVIEW, YUV, S1440P_4_3},
2542         };
2543 
2544         final int[][][] tables =
2545                 {legacyCombinations, limitedCombinations, burstCombinations, fullCombinations,
2546                  rawCombinations, level3Combinations, concurrentStreamCombinations,
2547                  ultraHighResolutionsCombinations, tenBitOutputCombinations,
2548                  previewStabilizationCombinations};
2549 
2550         final int[][][] useCaseTables = {streamUseCaseCombinations,
2551                 streamUseCaseCroppedRawCombinations};
2552 
2553         validityCheckConfigurationTables(tables);
2554         validityCheckConfigurationTables(useCaseTables, /*useCaseSpecified*/ true);
2555 
2556         for (String id : getCameraIdsUnderTest()) {
2557             openDevice(id);
2558             MandatoryStreamCombination[] combinations =
2559                     mStaticInfo.getCharacteristics().get(
2560                             CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS);
2561             if ((combinations == null) || (combinations.length == 0)) {
2562                 Log.i(TAG, "No mandatory stream combinations for camera: " + id + " skip test");
2563                 closeDevice(id);
2564                 continue;
2565             }
2566 
2567             MaxStreamSizes maxSizes = new MaxStreamSizes(mStaticInfo, id, mContext);
2568             try {
2569                 if (mStaticInfo.isColorOutputSupported()) {
2570                     for (int[] c : legacyCombinations) {
2571                         assertTrue(String.format("Expected static stream combination: %s not "
2572                                     + "found among the available mandatory combinations",
2573                                     maxSizes.combinationToString(c)),
2574                                 isMandatoryCombinationAvailable(c, maxSizes, combinations));
2575                     }
2576                 }
2577 
2578                 if (!mStaticInfo.isHardwareLevelLegacy()) {
2579                     if (mStaticInfo.isColorOutputSupported()) {
2580                         for (int[] c : limitedCombinations) {
2581                             assertTrue(String.format("Expected static stream combination: %s not "
2582                                         + "found among the available mandatory combinations",
2583                                         maxSizes.combinationToString(c)),
2584                                     isMandatoryCombinationAvailable(c, maxSizes, combinations));
2585                         }
2586                     }
2587 
2588                     if (mStaticInfo.isCapabilitySupported(
2589                             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
2590                         for (int[] c : burstCombinations) {
2591                             assertTrue(String.format("Expected static stream combination: %s not "
2592                                         + "found among the available mandatory combinations",
2593                                         maxSizes.combinationToString(c)),
2594                                     isMandatoryCombinationAvailable(c, maxSizes, combinations));
2595                         }
2596                     }
2597 
2598                     if (mStaticInfo.isHardwareLevelAtLeastFull()) {
2599                         for (int[] c : fullCombinations) {
2600                             assertTrue(String.format("Expected static stream combination: %s not "
2601                                         + "found among the available mandatory combinations",
2602                                         maxSizes.combinationToString(c)),
2603                                     isMandatoryCombinationAvailable(c, maxSizes, combinations));
2604                         }
2605                     }
2606 
2607                     if (mStaticInfo.isCapabilitySupported(
2608                             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
2609                         for (int[] c : rawCombinations) {
2610                             assertTrue(String.format("Expected static stream combination: %s not "
2611                                         + "found among the available mandatory combinations",
2612                                         maxSizes.combinationToString(c)),
2613                                     isMandatoryCombinationAvailable(c, maxSizes, combinations));
2614                         }
2615                     }
2616 
2617                     if (mStaticInfo.isHardwareLevelAtLeast(
2618                             CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)) {
2619                         for (int[] c: level3Combinations) {
2620                             assertTrue(String.format("Expected static stream combination: %s not "
2621                                         + "found among the available mandatory combinations ",
2622                                         maxSizes.combinationToString(c)),
2623                                     isMandatoryCombinationAvailable(c, maxSizes, combinations));
2624                         }
2625                     }
2626                 }
2627 
2628                 Set<Set<String>> concurrentCameraIdCombinations =
2629                         mCameraManager.getConcurrentCameraIds();
2630                 boolean isConcurrentCamera = false;
2631                 for (Set<String> concurrentCameraIdCombination : concurrentCameraIdCombinations) {
2632                     if (concurrentCameraIdCombination.contains(id)) {
2633                         isConcurrentCamera = true;
2634                         break;
2635                     }
2636                 }
2637 
2638                 if (isConcurrentCamera && mStaticInfo.isCapabilitySupported(
2639                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE)) {
2640                     MandatoryStreamCombination[] mandatoryConcurrentStreamCombinations =
2641                             mStaticInfo.getCharacteristics().get(
2642                                     CameraCharacteristics
2643                                             .SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS);
2644                     for (int[] c : concurrentStreamCombinations) {
2645                         assertTrue(String.format("Expected static stream combination: %s not "
2646                                     + "found among the available mandatory concurrent stream "
2647                                     + "combinations",
2648                                     maxSizes.combinationToString(c)),
2649                                 isMandatoryCombinationAvailable(c, maxSizes,
2650                                         mandatoryConcurrentStreamCombinations));
2651                     }
2652                 }
2653 
2654                 if (mStaticInfo.isCapabilitySupported(
2655                         CameraCharacteristics
2656                                 .REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR)) {
2657                     MandatoryStreamCombination[] maxResolutionStreamCombinations =
2658                         mStaticInfo.getCharacteristics().get(
2659                                 CameraCharacteristics
2660                                         .SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS);
2661                     for (int[] c : ultraHighResolutionsCombinations) {
2662                         assertTrue(String.format("Expected static stream combination: %s not "
2663                                     + "found among the available mandatory max resolution stream "
2664                                     + "combinations",
2665                                     maxSizes.combinationToString(c)),
2666                                 isMandatoryCombinationAvailable(c, maxSizes,
2667                                         maxResolutionStreamCombinations));
2668                     }
2669                 }
2670 
2671                 if (mStaticInfo.isCapabilitySupported(
2672                         CameraCharacteristics
2673                                 .REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT)) {
2674                     MandatoryStreamCombination[] mandatoryTenBitOutputCombinations =
2675                         mStaticInfo.getCharacteristics().get(
2676                             CameraCharacteristics
2677                                     .SCALER_MANDATORY_TEN_BIT_OUTPUT_STREAM_COMBINATIONS);
2678                     for (int[] c : tenBitOutputCombinations) {
2679                         assertTrue(String.format("Expected static stream combination: %s not "
2680                                     + "found among the available mandatory 10 bit output "
2681                                     + "combinations",
2682                                     maxSizes.combinationToString(c)),
2683                                 isMandatoryCombinationAvailable(c, maxSizes,
2684                                         mandatoryTenBitOutputCombinations));
2685                     }
2686                 }
2687 
2688                 if (mStaticInfo.isCapabilitySupported(
2689                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE)) {
2690                     MandatoryStreamCombination[] mandatoryStreamUseCaseCombinations =
2691                         mStaticInfo.getCharacteristics().get(
2692                                 CameraCharacteristics
2693                                         .SCALER_MANDATORY_USE_CASE_STREAM_COMBINATIONS);
2694                     for (int[] c : streamUseCaseCombinations) {
2695                         assertTrue(String.format("Expected static stream combination: %s not "
2696                                     + "found among the available mandatory stream use case "
2697                                     + "combinations",
2698                                     maxSizes.combinationToString(c, /*useCaseSpecified*/ true)),
2699                                 isMandatoryCombinationAvailable(c, maxSizes,
2700                                         /*isInput*/ false,  mandatoryStreamUseCaseCombinations,
2701                                         /*useCaseSpecified*/ true));
2702                     }
2703 
2704                     if (mStaticInfo.isCroppedRawStreamUseCaseSupported()) {
2705                         for (int[] c : streamUseCaseCroppedRawCombinations) {
2706                             assertTrue(String.format("Expected static stream combination: %s not "
2707                                         + "found among the available mandatory cropped RAW stream"
2708                                         + " use case combinations",
2709                                         maxSizes.combinationToString(c, /*useCaseSpecified*/ true)),
2710                                     isMandatoryCombinationAvailable(c, maxSizes,
2711                                             /*isInput*/ false,  mandatoryStreamUseCaseCombinations,
2712                                             /*useCaseSpecified*/ true));
2713                         }
2714                     }
2715                 }
2716 
2717                 if (mStaticInfo.isPreviewStabilizationSupported()) {
2718                     MandatoryStreamCombination[] mandatoryPreviewStabilizationCombinations =
2719                         mStaticInfo.getCharacteristics().get(
2720                             CameraCharacteristics
2721                                 .SCALER_MANDATORY_PREVIEW_STABILIZATION_OUTPUT_STREAM_COMBINATIONS);
2722                     for (int[] c : previewStabilizationCombinations) {
2723                         assertTrue(String.format("Expected static stream combination: %s not "
2724                                     + "found among the available mandatory preview stabilization"
2725                                     + "combinations",
2726                                     maxSizes.combinationToString(c)),
2727                                 isMandatoryCombinationAvailable(c, maxSizes,
2728                                         mandatoryPreviewStabilizationCombinations));
2729                     }
2730                 }
2731             } finally {
2732                 closeDevice(id);
2733             }
2734         }
2735     }
2736 
2737     /**
2738      * Test for making sure that all expected reprocessable mandatory stream combinations are
2739      * present and advertised accordingly.
2740      */
2741     @Test
testVerifyReprocessMandatoryOutputCombinationTables()2742     public void testVerifyReprocessMandatoryOutputCombinationTables() throws Exception {
2743         final int[][] limitedCombinations = {
2744             // Input           Outputs
2745             {PRIV, MAXIMUM,    JPEG, MAXIMUM},
2746             {YUV , MAXIMUM,    JPEG, MAXIMUM},
2747             {PRIV, MAXIMUM,    PRIV, PREVIEW, JPEG, MAXIMUM},
2748             {YUV , MAXIMUM,    PRIV, PREVIEW, JPEG, MAXIMUM},
2749             {PRIV, MAXIMUM,    YUV , PREVIEW, JPEG, MAXIMUM},
2750             {YUV , MAXIMUM,    YUV , PREVIEW, JPEG, MAXIMUM},
2751             {PRIV, MAXIMUM,    YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
2752             {YUV,  MAXIMUM,    YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
2753         };
2754 
2755         final int[][] fullCombinations = {
2756             // Input           Outputs
2757             {YUV , MAXIMUM,    PRIV, PREVIEW},
2758             {YUV , MAXIMUM,    YUV , PREVIEW},
2759             {PRIV, MAXIMUM,    PRIV, PREVIEW, YUV , RECORD},
2760             {YUV , MAXIMUM,    PRIV, PREVIEW, YUV , RECORD},
2761             {PRIV, MAXIMUM,    PRIV, PREVIEW, YUV , MAXIMUM},
2762             {PRIV, MAXIMUM,    YUV , PREVIEW, YUV , MAXIMUM},
2763             {PRIV, MAXIMUM,    PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
2764             {YUV , MAXIMUM,    PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
2765         };
2766 
2767         final int[][] rawCombinations = {
2768             // Input           Outputs
2769             {PRIV, MAXIMUM,    YUV , PREVIEW, RAW , MAXIMUM},
2770             {YUV , MAXIMUM,    YUV , PREVIEW, RAW , MAXIMUM},
2771             {PRIV, MAXIMUM,    PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
2772             {YUV , MAXIMUM,    PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
2773             {PRIV, MAXIMUM,    YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
2774             {YUV , MAXIMUM,    YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
2775             {PRIV, MAXIMUM,    PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
2776             {YUV , MAXIMUM,    PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
2777             {PRIV, MAXIMUM,    YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
2778             {YUV , MAXIMUM,    YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
2779         };
2780 
2781         final int[][] level3Combinations = {
2782             // Input          Outputs
2783             // In-app viewfinder analysis with YUV->YUV ZSL and RAW
2784             {YUV , MAXIMUM,   PRIV, PREVIEW, PRIV, VGA, RAW, MAXIMUM},
2785             // In-app viewfinder analysis with PRIV->JPEG ZSL and RAW
2786             {PRIV, MAXIMUM,   PRIV, PREVIEW, PRIV, VGA, RAW, MAXIMUM, JPEG, MAXIMUM},
2787             // In-app viewfinder analysis with YUV->JPEG ZSL and RAW
2788             {YUV , MAXIMUM,   PRIV, PREVIEW, PRIV, VGA, RAW, MAXIMUM, JPEG, MAXIMUM},
2789         };
2790 
2791         final int[][] ultraHighResolutionCombinations = {
2792             // Input           Outputs
2793             // RAW remosaic reprocessing with separate preview
2794             {RAW, MAX_RES,     PRIV, PREVIEW},
2795             {RAW, MAX_RES,     YUV, PREVIEW},
2796             // Ultra high res RAW -> JPEG / YUV with separate preview
2797             {RAW, MAX_RES,     PRIV, PREVIEW, JPEG, MAX_RES},
2798             {RAW, MAX_RES,     PRIV, PREVIEW, YUV, MAX_RES},
2799             {RAW, MAX_RES,     YUV, PREVIEW, JPEG, MAX_RES},
2800             {RAW, MAX_RES,     YUV, PREVIEW, YUV, MAX_RES},
2801             // Ultra high res PRIV / YUV -> YUV / JPEG reprocessing with separate preview
2802             {YUV, MAX_RES,     YUV, PREVIEW, JPEG, MAX_RES},
2803             {YUV, MAX_RES,     PRIV, PREVIEW, JPEG, MAX_RES},
2804             {PRIV, MAX_RES,    YUV, PREVIEW, JPEG, MAX_RES},
2805             {PRIV, MAX_RES,    PRIV, PREVIEW, JPEG, MAX_RES},
2806         };
2807 
2808         final int[][][] TABLES =
2809                 {limitedCombinations, fullCombinations, rawCombinations, level3Combinations,
2810                  ultraHighResolutionCombinations};
2811 
2812         validityCheckConfigurationTables(TABLES);
2813 
2814         for (String id : getCameraIdsUnderTest()) {
2815             openDevice(id);
2816             MandatoryStreamCombination[] cs = mStaticInfo.getCharacteristics().get(
2817                     CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS);
2818             if ((cs == null) || (cs.length == 0)) {
2819                 Log.i(TAG, "No mandatory stream combinations for camera: " + id + " skip test");
2820                 closeDevice(id);
2821                 continue;
2822             }
2823 
2824             boolean supportYuvReprocess = mStaticInfo.isCapabilitySupported(
2825                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
2826             boolean supportOpaqueReprocess = mStaticInfo.isCapabilitySupported(
2827                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
2828             if (!supportYuvReprocess && !supportOpaqueReprocess) {
2829                 Log.i(TAG, "No reprocess support for camera: " + id + " skip test");
2830                 closeDevice(id);
2831                 continue;
2832             }
2833 
2834             MaxStreamSizes maxSizes = new MaxStreamSizes(mStaticInfo, id, mContext);
2835             try {
2836                 for (int[] c : limitedCombinations) {
2837                     assertTrue(String.format("Expected static reprocessable stream combination:" +
2838                                 "%s not found among the available mandatory combinations",
2839                                 maxSizes.reprocessCombinationToString(c)),
2840                             isMandatoryCombinationAvailable(c, maxSizes, /*isInput*/ true, cs));
2841                 }
2842 
2843                 if (mStaticInfo.isHardwareLevelAtLeastFull()) {
2844                     for (int[] c : fullCombinations) {
2845                         assertTrue(String.format(
2846                                     "Expected static reprocessable stream combination:" +
2847                                     "%s not found among the available mandatory combinations",
2848                                     maxSizes.reprocessCombinationToString(c)),
2849                                 isMandatoryCombinationAvailable(c, maxSizes, /*isInput*/ true, cs));
2850                     }
2851                 }
2852 
2853                 if (mStaticInfo.isCapabilitySupported(
2854                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
2855                     for (int[] c : rawCombinations) {
2856                         assertTrue(String.format(
2857                                     "Expected static reprocessable stream combination:" +
2858                                     "%s not found among the available mandatory combinations",
2859                                     maxSizes.reprocessCombinationToString(c)),
2860                                 isMandatoryCombinationAvailable(c, maxSizes, /*isInput*/ true, cs));
2861                     }
2862                 }
2863 
2864                 if (mStaticInfo.isHardwareLevelAtLeast(
2865                             CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)) {
2866                     for (int[] c : level3Combinations) {
2867                         assertTrue(String.format(
2868                                     "Expected static reprocessable stream combination:" +
2869                                     "%s not found among the available mandatory combinations",
2870                                     maxSizes.reprocessCombinationToString(c)),
2871                                 isMandatoryCombinationAvailable(c, maxSizes, /*isInput*/ true, cs));
2872                     }
2873                 }
2874 
2875                 if (mStaticInfo.isCapabilitySupported(
2876                         CameraCharacteristics
2877                                 .REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR)) {
2878                     MandatoryStreamCombination[] maxResolutionCombinations =
2879                             mStaticInfo.getCharacteristics().get(
2880                                     CameraCharacteristics
2881                                         .SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS);
2882                     for (int[] c : ultraHighResolutionCombinations) {
2883                         assertTrue(String.format(
2884                                 "Expected static reprocessable stream combination:"
2885                                     + "%s not found among the available mandatory max resolution"
2886                                     + "combinations",
2887                                     maxSizes.reprocessCombinationToString(c)),
2888                                 isMandatoryCombinationAvailable(c, maxSizes, /*isInput*/ true,
2889                                         maxResolutionCombinations));
2890                     }
2891                 }
2892             } finally {
2893                 closeDevice(id);
2894             }
2895         }
2896     }
2897 
isMandatoryCombinationAvailable(final int[] combination, final MaxStreamSizes maxSizes, final MandatoryStreamCombination[] availableCombinations)2898     private boolean isMandatoryCombinationAvailable(final int[] combination,
2899             final MaxStreamSizes maxSizes,
2900             final MandatoryStreamCombination[] availableCombinations) {
2901         return isMandatoryCombinationAvailable(combination, maxSizes, /*isInput*/ false,
2902                 availableCombinations, /*useCaseSpecified*/ false);
2903     }
2904 
isMandatoryCombinationAvailable(final int[] combination, final MaxStreamSizes maxSizes, boolean isInput, final MandatoryStreamCombination[] availableCombinations)2905     private boolean isMandatoryCombinationAvailable(final int[] combination,
2906             final MaxStreamSizes maxSizes, boolean isInput,
2907             final MandatoryStreamCombination[] availableCombinations) {
2908         return isMandatoryCombinationAvailable(combination, maxSizes, isInput,
2909                 availableCombinations, /*useCaseSpecified*/ false);
2910     }
2911 
isMandatoryCombinationAvailable(final int[] combination, final MaxStreamSizes maxSizes, boolean isInput, final MandatoryStreamCombination[] availableCombinations, boolean useCaseSpecified)2912     private boolean isMandatoryCombinationAvailable(final int[] combination,
2913             final MaxStreamSizes maxSizes, boolean isInput,
2914             final MandatoryStreamCombination[] availableCombinations, boolean useCaseSpecified) {
2915         boolean supportYuvReprocess = mStaticInfo.isCapabilitySupported(
2916                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
2917         boolean supportOpaqueReprocess = mStaticInfo.isCapabilitySupported(
2918                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
2919         // Static combinations to be verified can be composed of multiple entries
2920         // that have the following layout (format, size). In case "isInput" is set,
2921         // the first stream configuration entry will contain the input format and size
2922         // as well as the first matching output.
2923         // For combinations that contain streamUseCase, the layout will be (format, size, useCase).
2924         int streamCount = useCaseSpecified ? combination.length / 3 : combination.length / 2;
2925 
2926         List<Pair<Pair<Integer, Boolean>, Size>> currentCombination =
2927                 new ArrayList<Pair<Pair<Integer, Boolean>, Size>>(streamCount);
2928         List<Integer> streamUseCases = new ArrayList<Integer>(streamCount);
2929         int i = 0;
2930         while (i < combination.length) {
2931             if (isInput && (i == 0)) {
2932                 // Skip the combination if the format is not supported for reprocessing.
2933                 if ((combination[i] == YUV && !supportYuvReprocess) ||
2934                         (combination[i] == PRIV && !supportOpaqueReprocess)) {
2935                     return true;
2936                 }
2937                 // Skip the combination if for MAX_RES size, the maximum resolution stream config
2938                 // map doesn't have the given format in getInputFormats().
2939                 if (combination[i + 1] == MAX_RES) {
2940                     StreamConfigurationMap maxResolutionStreamConfigMap =
2941                             mStaticInfo.getCharacteristics().get(
2942                                     CameraCharacteristics
2943                                             .SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION);
2944                     int[] inputFormats = maxResolutionStreamConfigMap.getInputFormats();
2945                     int type = combination[i];
2946                     if (!Arrays.stream(inputFormats).anyMatch(index -> index == type)) {
2947                         return true;
2948                     }
2949                 }
2950                 Size sz = maxSizes.getMaxInputSizeForFormat(combination[i], combination[i + 1]);
2951                 currentCombination.add(Pair.create(Pair.create(new Integer(combination[i]),
2952                             new Boolean(true)), sz));
2953                 currentCombination.add(Pair.create(Pair.create(new Integer(combination[i]),
2954                             new Boolean(false)), sz));
2955             } else {
2956                 Size sz = maxSizes.getOutputSizeForFormat(combination[i], combination[i+1]);
2957                 currentCombination.add(Pair.create(Pair.create(new Integer(combination[i]),
2958                             new Boolean(false)), sz));
2959                 if (useCaseSpecified) {
2960                     streamUseCases.add(combination[i + 2]);
2961                 }
2962             }
2963             i += 2;
2964             if (useCaseSpecified) {
2965                 i += 1;
2966             }
2967         }
2968 
2969         for (MandatoryStreamCombination c : availableCombinations) {
2970             List<MandatoryStreamInformation> streamInfoList = c.getStreamsInformation();
2971             if ((streamInfoList.size() == currentCombination.size()) &&
2972                     (isInput == c.isReprocessable())) {
2973                 ArrayList<Pair<Pair<Integer, Boolean>, Size>> expected =
2974                         new ArrayList<Pair<Pair<Integer, Boolean>, Size>>(currentCombination);
2975                 ArrayList<Integer> expectedStreamUseCases = new ArrayList<Integer>(streamUseCases);
2976 
2977                 for (MandatoryStreamInformation streamInfo : streamInfoList) {
2978                     Size maxSize = CameraTestUtils.getMaxSize(
2979                             streamInfo.getAvailableSizes().toArray(new Size[0]));
2980                     Pair p = Pair.create(Pair.create(new Integer(streamInfo.getFormat()),
2981                             new Boolean(streamInfo.isInput())), maxSize);
2982                     if (expected.contains(p)) {
2983                         expected.remove(p);
2984                     }
2985                     if (useCaseSpecified) {
2986                         int streamUseCase = (int) streamInfo.getStreamUseCase();
2987                         if (expectedStreamUseCases.contains(streamUseCase)) {
2988                             expectedStreamUseCases.remove(Integer.valueOf(streamUseCase));
2989                         }
2990                     }
2991                 }
2992 
2993                 if (expected.isEmpty() && (!useCaseSpecified || expectedStreamUseCases.isEmpty())) {
2994                     return true;
2995                 }
2996             }
2997         }
2998 
2999         return false;
3000     }
3001 
3002     /**
3003      * Verify correctness of the configuration tables.
3004      */
validityCheckConfigurationTables(final int[][][] tables)3005     private void validityCheckConfigurationTables(final int[][][] tables) throws Exception {
3006         validityCheckConfigurationTables(tables, false);
3007     }
3008 
validityCheckConfigurationTables(final int[][][] tables, boolean useCaseSpecified)3009     private void validityCheckConfigurationTables(final int[][][] tables, boolean useCaseSpecified)
3010             throws Exception {
3011         int tableIdx = 0;
3012         for (int[][] table : tables) {
3013             int rowIdx = 0;
3014             for (int[] row : table) {
3015                 if (!useCaseSpecified) {
3016                     assertTrue(String.format("Odd number of entries for table %d row %d: %s ",
3017                                     tableIdx, rowIdx, Arrays.toString(row)),
3018                             (row.length % 2) == 0);
3019                 } else {
3020                     assertTrue(String.format("Incorrect number entries for table with use case "
3021                                              + "specified %d row %d: %s ",
3022                                     tableIdx, rowIdx, Arrays.toString(row)),
3023                             (row.length % 3) == 0);
3024                 }
3025 
3026                 int i = 0;
3027                 while (i < row.length) {
3028                     int format = row[i];
3029                     int maxSize = row[i + 1];
3030                     assertTrue(String.format("table %d row %d index %d format not valid: %d",
3031                                     tableIdx, rowIdx, i, format),
3032                             format == PRIV || format == JPEG || format == YUV
3033                                     || format == RAW);
3034                     assertTrue(String.format("table %d row %d index %d max size not valid: %d",
3035                                     tableIdx, rowIdx, i + 1, maxSize),
3036                             maxSize == PREVIEW || maxSize == RECORD
3037                                     || maxSize == MAXIMUM || maxSize == VGA || maxSize == S720P
3038                                     || maxSize == S1440P_4_3 || maxSize == MAX_RES);
3039                     if (useCaseSpecified) {
3040                         int useCase = row[i + 2];
3041                         assertTrue(String.format("table %d row %d index %d use case not valid: %d",
3042                                         tableIdx, rowIdx, i + 2, useCase),
3043                                 useCase == USE_CASE_PREVIEW
3044                                         || useCase == USE_CASE_PREVIEW_VIDEO_STILL
3045                                         || useCase == USE_CASE_STILL_CAPTURE
3046                                         || useCase == USE_CASE_VIDEO_CALL
3047                                         || useCase == USE_CASE_VIDEO_RECORD
3048                                         || useCase == USE_CASE_CROPPED_RAW);
3049                         i += 3;
3050                     } else {
3051                         i += 2;
3052                     }
3053                 }
3054                 rowIdx++;
3055             }
3056             tableIdx++;
3057         }
3058     }
3059 }
3060