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