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