• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.camera2.cts;
18 
19 import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
20 import static android.hardware.camera2.cts.helpers.AssertHelpers.assertArrayContains;
21 import static android.hardware.camera2.cts.helpers.AssertHelpers.assertArrayContainsAnyOf;
22 import static android.hardware.camera2.cts.helpers.AssertHelpers.assertCollectionContainsAnyOf;
23 import static android.hardware.cts.helpers.CameraUtils.matchParametersToCharacteristics;
24 
25 import static junit.framework.Assert.assertEquals;
26 import static junit.framework.Assert.assertFalse;
27 import static junit.framework.Assert.assertNotNull;
28 import static junit.framework.Assert.assertTrue;
29 import static junit.framework.Assert.fail;
30 
31 import static org.junit.Assume.assumeFalse;
32 import static org.junit.Assume.assumeTrue;
33 import static org.mockito.Mockito.any;
34 import static org.mockito.Mockito.mock;
35 import static org.mockito.Mockito.never;
36 import static org.mockito.Mockito.reset;
37 import static org.mockito.Mockito.timeout;
38 import static org.mockito.Mockito.verify;
39 
40 import android.content.Context;
41 import android.content.Intent;
42 import android.content.pm.PackageManager;
43 import android.graphics.ColorSpace;
44 import android.graphics.ImageFormat;
45 import android.graphics.Rect;
46 import android.graphics.SurfaceTexture;
47 import android.hardware.Camera;
48 import android.hardware.DataSpace;
49 import android.hardware.HardwareBuffer;
50 import android.hardware.camera2.CameraCaptureSession;
51 import android.hardware.camera2.CameraCharacteristics;
52 import android.hardware.camera2.CameraCharacteristics.Key;
53 import android.hardware.camera2.CameraDevice;
54 import android.hardware.camera2.CameraExtensionCharacteristics;
55 import android.hardware.camera2.CameraMetadata;
56 import android.hardware.camera2.CaptureFailure;
57 import android.hardware.camera2.CaptureRequest;
58 import android.hardware.camera2.CaptureResult;
59 import android.hardware.camera2.TotalCaptureResult;
60 import android.hardware.camera2.cts.helpers.StaticMetadata;
61 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
62 import android.hardware.camera2.params.BlackLevelPattern;
63 import android.hardware.camera2.params.ColorSpaceProfiles;
64 import android.hardware.camera2.params.ColorSpaceTransform;
65 import android.hardware.camera2.params.DeviceStateSensorOrientationMap;
66 import android.hardware.camera2.params.DynamicRangeProfiles;
67 import android.hardware.camera2.params.OutputConfiguration;
68 import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
69 import android.hardware.camera2.params.SessionConfiguration;
70 import android.hardware.camera2.params.StreamConfigurationMap;
71 import android.hardware.cts.helpers.CameraUtils;
72 import android.media.CamcorderProfile;
73 import android.media.Image;
74 import android.media.ImageReader;
75 import android.mediapc.cts.common.PerformanceClassEvaluator;
76 import android.mediapc.cts.common.Requirements;
77 import android.mediapc.cts.common.Requirements.CameraConcurrentRearFrontStreamingRequirement;
78 import android.mediapc.cts.common.Requirements.CameraDynamicRange10BitRequirement;
79 import android.mediapc.cts.common.Requirements.CameraFaceDetectionRequirement;
80 import android.mediapc.cts.common.Requirements.CameraHardwareLevelRequirement;
81 import android.mediapc.cts.common.Requirements.CameraJPEGRRequirement;
82 import android.mediapc.cts.common.Requirements.CameraLogicalMultiCameraRequirement;
83 import android.mediapc.cts.common.Requirements.CameraNightModeExtensionRequirement;
84 import android.mediapc.cts.common.Requirements.CameraPreviewStabilizationRequirement;
85 import android.mediapc.cts.common.Requirements.CameraRAWCapabilityRequirement;
86 import android.mediapc.cts.common.Requirements.CameraSlowMotionRequirement;
87 import android.mediapc.cts.common.Requirements.CameraStreamUseCaseRequirement;
88 import android.mediapc.cts.common.Requirements.CameraUltrawideZoomRatioRequirement;
89 import android.mediapc.cts.common.Requirements.PrimaryFrontCameraResolutionAndFrameRateRequirement;
90 import android.mediapc.cts.common.Requirements.PrimaryRearCameraResolutionAndFrameRateRequirement;
91 import android.mediapc.cts.common.Requirements.TimestampSourceRealtimeRequirement;
92 import android.os.Build;
93 import android.platform.test.annotations.AppModeFull;
94 import android.platform.test.annotations.RequiresFlagsEnabled;
95 import android.platform.test.flag.junit.CheckFlagsRule;
96 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
97 import android.util.ArraySet;
98 import android.util.Log;
99 import android.util.Pair;
100 import android.util.Patterns;
101 import android.util.Range;
102 import android.util.Rational;
103 import android.util.Size;
104 import android.util.SizeF;
105 import android.view.Display;
106 import android.view.Surface;
107 import android.view.WindowManager;
108 import android.view.WindowMetrics;
109 
110 import androidx.camera.core.CameraSelector;
111 import androidx.camera.extensions.ExtensionMode;
112 import androidx.camera.extensions.ExtensionsManager;
113 import androidx.camera.lifecycle.ProcessCameraProvider;
114 import androidx.test.rule.ActivityTestRule;
115 
116 import com.android.compatibility.common.util.ApiTest;
117 import com.android.compatibility.common.util.CddTest;
118 import com.android.internal.camera.flags.Flags;
119 
120 import com.google.common.util.concurrent.ListenableFuture;
121 
122 import org.junit.Rule;
123 import org.junit.Test;
124 import org.junit.rules.TestName;
125 import org.junit.runner.RunWith;
126 import org.junit.runners.Parameterized;
127 
128 import java.util.ArrayList;
129 import java.util.Arrays;
130 import java.util.HashSet;
131 import java.util.List;
132 import java.util.Map;
133 import java.util.Objects;
134 import java.util.Set;
135 import java.util.concurrent.ExecutionException;
136 import java.util.concurrent.TimeUnit;
137 import java.util.concurrent.TimeoutException;
138 import java.util.regex.Matcher;
139 import java.util.regex.Pattern;
140 
141 
142 /**
143  * Extended tests for static camera characteristics.
144  */
145 @RunWith(Parameterized.class)
146 public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase {
147     private static final String TAG = "ExChrsTest"; // must be short so next line doesn't throw
148     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
149 
150     private static final String PREFIX_ANDROID = "android";
151 
152     /*
153      * Constants for static RAW metadata.
154      */
155     private static final int MIN_ALLOWABLE_WHITELEVEL = 32; // must have sensor bit depth > 5
156 
157     @Rule
158     public final TestName mTestName = new TestName();
159 
160     private List<CameraCharacteristics> mCharacteristics;
161 
162     private static final Size FULLHD = new Size(1920, 1080);
163     private static final Size FULLHD_ALT = new Size(1920, 1088);
164     private static final Size HD = new Size(1280, 720);
165     private static final Size VGA = new Size(640, 480);
166     private static final Size QVGA = new Size(320, 240);
167     private static final Size UHD = new Size(3840, 2160);
168     private static final Size DC4K = new Size(4096, 2160);
169 
170     private static final long MIN_BACK_SENSOR_RESOLUTION = 2000000;
171     private static final long MIN_FRONT_SENSOR_RESOLUTION = VGA.getHeight() * VGA.getWidth();
172     private static final long LOW_LATENCY_THRESHOLD_MS = 200;
173     private static final float LATENCY_TOLERANCE_FACTOR = 1.1f; // 10% tolerance
174     private static final int MAX_NUM_IMAGES = 5;
175     private static final long PREVIEW_RUN_MS = 500;
176     private static final long FRAME_DURATION_30FPS_NSEC = (long) 1e9 / 30;
177     private static final long WAIT_TIMEOUT_IN_MS = 10_000;
178     private static final int CONFIGURE_TIMEOUT = 5000; // ms
179     private static final int CAPTURE_TIMEOUT = 1500; // ms
180 
181     private static final long MIN_UHR_SENSOR_RESOLUTION = 24000000;
182     /*
183      * HW Levels short hand
184      */
185     private static final int LEGACY = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
186     private static final int LIMITED = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
187     private static final int FULL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
188     private static final int LEVEL_3 = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3;
189     private static final int EXTERNAL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
190     private static final int OPT = Integer.MAX_VALUE;  // For keys that are optional on all hardware levels.
191 
192     /*
193      * Capabilities short hand
194      */
195     private static final int NONE = -1;
196     private static final int BC =
197             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
198     private static final int MANUAL_SENSOR =
199             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR;
200     private static final int MANUAL_POSTPROC =
201             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING;
202     private static final int RAW =
203             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW;
204     private static final int YUV_REPROCESS =
205             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING;
206     private static final int OPAQUE_REPROCESS =
207             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
208     private static final int CONSTRAINED_HIGH_SPEED =
209             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO;
210     private static final int DYNAMIC_RANGE_TEN_BIT =
211             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT;
212     private static final int FACE_DETECTION_MODE_SIMPLE =
213             CameraCharacteristics.STATISTICS_FACE_DETECT_MODE_SIMPLE;
214     private static final int FACE_DETECTION_MODE_FULL =
215             CameraCharacteristics.STATISTICS_FACE_DETECT_MODE_FULL;
216     private static final int MONOCHROME =
217             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME;
218     private static final int HIGH_SPEED_FPS_LOWER_MIN = 30;
219     private static final int HIGH_SPEED_FPS_UPPER_MIN = 120;
220 
221     @Rule
222     public final ActivityTestRule<EmptyActivity> mActivityRule = new ActivityTestRule<>(
223             EmptyActivity.class, false, false);
224 
225     @Rule
226     public final CheckFlagsRule mCheckFlagsRule =
227             DeviceFlagsValueProvider.createCheckFlagsRule();
228 
229     @Override
setUp()230     public void setUp() throws Exception {
231         super.setUp();
232         mCharacteristics = new ArrayList<>();
233         String[] allCameraIds = getAllCameraIds();
234         for (int i = 0; i < allCameraIds.length; i++) {
235             mCharacteristics.add(mAllStaticInfo.get(allCameraIds[i]).getCharacteristics());
236         }
237     }
238 
239     @Override
tearDown()240     public void tearDown() throws Exception {
241         super.tearDown();
242         mCharacteristics = null;
243     }
244 
245     /**
246      * Test that the available stream configurations contain a few required formats and sizes.
247      */
248     @CddTest(requirement="7.5.1/C-1-2")
249     @Test
testAvailableStreamConfigs()250     public void testAvailableStreamConfigs() throws Exception {
251         boolean firstBackFacingCamera = true;
252         String[] allCameraIds = getAllCameraIds();
253         for (int i = 0; i < allCameraIds.length; i++) {
254             CameraCharacteristics c = mCharacteristics.get(i);
255             StreamConfigurationMap config =
256                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
257             assertNotNull(String.format("No stream configuration map found for: ID %s",
258                     allCameraIds[i]), config);
259             int[] outputFormats = config.getOutputFormats();
260 
261             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
262             assertNotNull("android.request.availableCapabilities must never be null",
263                     actualCapabilities);
264 
265             // Check required formats exist (JPEG, and YUV_420_888).
266             if (!arrayContains(actualCapabilities, BC)) {
267                 Log.i(TAG, "Camera " + allCameraIds[i] +
268                     ": BACKWARD_COMPATIBLE capability not supported, skipping test");
269                 continue;
270             }
271 
272             boolean isMonochromeWithY8 = arrayContains(actualCapabilities, MONOCHROME)
273                     && arrayContains(outputFormats, ImageFormat.Y8);
274             boolean isHiddenPhysicalCamera = !arrayContains(getCameraIdsUnderTest(), allCameraIds[i]);
275             boolean supportHeic = arrayContains(outputFormats, ImageFormat.HEIC);
276 
277             assertArrayContains(
278                     String.format("No valid YUV_420_888 preview formats found for: ID %s",
279                             allCameraIds[i]), outputFormats, ImageFormat.YUV_420_888);
280             if (isMonochromeWithY8) {
281                 assertArrayContains(
282                         String.format("No valid Y8 preview formats found for: ID %s",
283                                 allCameraIds[i]), outputFormats, ImageFormat.Y8);
284             }
285             assertArrayContains(String.format("No JPEG image format for: ID %s",
286                     allCameraIds[i]), outputFormats, ImageFormat.JPEG);
287 
288             Size[] yuvSizes = config.getOutputSizes(ImageFormat.YUV_420_888);
289             Size[] y8Sizes = config.getOutputSizes(ImageFormat.Y8);
290             Size[] jpegSizes = config.getOutputSizes(ImageFormat.JPEG);
291             Size[] heicSizes = config.getOutputSizes(ImageFormat.HEIC);
292             Size[] privateSizes = config.getOutputSizes(ImageFormat.PRIVATE);
293 
294             CameraTestUtils.assertArrayNotEmpty(yuvSizes,
295                     String.format("No sizes for preview format %x for: ID %s",
296                             ImageFormat.YUV_420_888, allCameraIds[i]));
297             if (isMonochromeWithY8) {
298                 CameraTestUtils.assertArrayNotEmpty(y8Sizes,
299                     String.format("No sizes for preview format %x for: ID %s",
300                             ImageFormat.Y8, allCameraIds[i]));
301             }
302 
303             Rect activeRect = CameraTestUtils.getValueNotNull(
304                     c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
305             Size pixelArraySize = CameraTestUtils.getValueNotNull(
306                     c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
307 
308             int activeArrayHeight = activeRect.height();
309             int activeArrayWidth = activeRect.width();
310             long sensorResolution = pixelArraySize.getHeight() * pixelArraySize.getWidth() ;
311             Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
312             assertNotNull("Can't get lens facing info for camera id: " + allCameraIds[i],
313                     lensFacing);
314 
315             // Check that the sensor sizes are atleast what the CDD specifies
316             switch(lensFacing) {
317                 case CameraCharacteristics.LENS_FACING_FRONT:
318                     assertTrue("Front Sensor resolution should be at least " +
319                             MIN_FRONT_SENSOR_RESOLUTION + " pixels, is "+ sensorResolution,
320                             sensorResolution >= MIN_FRONT_SENSOR_RESOLUTION);
321                     break;
322                 case CameraCharacteristics.LENS_FACING_BACK:
323                     if (firstBackFacingCamera) {
324                         assertTrue("Back Sensor resolution should be at least "
325                                 + MIN_BACK_SENSOR_RESOLUTION +
326                                 " pixels, is "+ sensorResolution,
327                                 sensorResolution >= MIN_BACK_SENSOR_RESOLUTION);
328                         firstBackFacingCamera = false;
329                     }
330                     break;
331                 default:
332                     break;
333             }
334 
335             Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
336 
337             if (activeArrayWidth >= FULLHD.getWidth() &&
338                     activeArrayHeight >= FULLHD.getHeight()) {
339                 assertArrayContainsAnyOf(String.format(
340                         "Required FULLHD size not found for format %x for: ID %s",
341                         ImageFormat.JPEG, allCameraIds[i]), jpegSizes,
342                         new Size[] {FULLHD, FULLHD_ALT});
343                 if (supportHeic) {
344                     assertArrayContainsAnyOf(String.format(
345                             "Required FULLHD size not found for format %x for: ID %s",
346                             ImageFormat.HEIC, allCameraIds[i]), heicSizes,
347                             new Size[] {FULLHD, FULLHD_ALT});
348                 }
349             }
350 
351             boolean isPrimaryRear = CameraTestUtils.isPrimaryRearFacingCamera(
352                     mCameraManager, allCameraIds[i]);
353             boolean isPrimaryFront = CameraTestUtils.isPrimaryFrontFacingCamera(
354                     mCameraManager, allCameraIds[i]);
355             boolean isPrimaryCamera = isPrimaryFront || isPrimaryRear;
356             // Skip check for < 1080p JPEG sizes for media performance class level >= 31
357             // since SDK 31 requires a minimum size of 1080p for JPEG
358             if (Build.VERSION.MEDIA_PERFORMANCE_CLASS < Build.VERSION_CODES.S
359                     || !isPrimaryCamera) {
360                 if (activeArrayWidth >= HD.getWidth()
361                         && activeArrayHeight >= HD.getHeight()) {
362                     assertArrayContains(String.format(
363                             "Required HD size not found for format %x for: ID %s",
364                             ImageFormat.JPEG, allCameraIds[i]), jpegSizes, HD);
365                     if (supportHeic) {
366                         assertArrayContains(String.format(
367                                 "Required HD size not found for format %x for: ID %s",
368                                 ImageFormat.HEIC, allCameraIds[i]), heicSizes, HD);
369                     }
370                 }
371 
372                 if (activeArrayWidth >= VGA.getWidth()
373                         && activeArrayHeight >= VGA.getHeight()) {
374                     assertArrayContains(String.format(
375                             "Required VGA size not found for format %x for: ID %s",
376                             ImageFormat.JPEG, allCameraIds[i]), jpegSizes, VGA);
377                     if (supportHeic) {
378                         assertArrayContains(String.format(
379                                 "Required VGA size not found for format %x for: ID %s",
380                                 ImageFormat.HEIC, allCameraIds[i]), heicSizes, VGA);
381                     }
382                 }
383 
384                 if (activeArrayWidth >= QVGA.getWidth()
385                         && activeArrayHeight >= QVGA.getHeight()) {
386                     assertArrayContains(String.format(
387                             "Required QVGA size not found for format %x for: ID %s",
388                             ImageFormat.JPEG, allCameraIds[i]), jpegSizes, QVGA);
389                     if (supportHeic) {
390                         assertArrayContains(String.format(
391                                 "Required QVGA size not found for format %x for: ID %s",
392                                 ImageFormat.HEIC, allCameraIds[i]), heicSizes, QVGA);
393                     }
394                 }
395             }
396 
397             ArrayList<Size> jpegSizesList = new ArrayList<>(Arrays.asList(jpegSizes));
398             ArrayList<Size> yuvSizesList = new ArrayList<>(Arrays.asList(yuvSizes));
399             ArrayList<Size> privateSizesList = new ArrayList<>(Arrays.asList(privateSizes));
400             boolean isExternalCamera = (hwLevel ==
401                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
402             Size maxVideoSize = null;
403             if (isExternalCamera || isHiddenPhysicalCamera) {
404                 // TODO: for now, use FULLHD 30 as largest possible video size for external camera.
405                 // For hidden physical camera, since we don't require CamcorderProfile to be
406                 // available, use FULLHD 30 as maximum video size as well.
407                 List<Size> videoSizes = CameraTestUtils.getSupportedVideoSizes(
408                         allCameraIds[i], mCameraManager, FULLHD);
409                 for (Size sz : videoSizes) {
410                     long minFrameDuration = config.getOutputMinFrameDuration(
411                             android.media.MediaRecorder.class, sz);
412                     // Give some margin for rounding error
413                     if (minFrameDuration < (1e9 / 29.9)) {
414                         maxVideoSize = sz;
415                         break;
416                     }
417                 }
418             } else {
419                 int cameraId = Integer.valueOf(allCameraIds[i]);
420                 CamcorderProfile maxVideoProfile = CamcorderProfile.get(
421                         cameraId, CamcorderProfile.QUALITY_HIGH);
422                 maxVideoSize = new Size(
423                         maxVideoProfile.videoFrameWidth, maxVideoProfile.videoFrameHeight);
424             }
425             if (maxVideoSize == null) {
426                 fail("Camera " + allCameraIds[i] + " does not support any 30fps video output");
427             }
428 
429             // Handle FullHD special case first
430             if (jpegSizesList.contains(FULLHD)) {
431                 if (compareHardwareLevel(hwLevel, LEVEL_3) >= 0 || hwLevel == FULL ||
432                         (hwLevel == LIMITED &&
433                         maxVideoSize.getWidth() >= FULLHD.getWidth() &&
434                         maxVideoSize.getHeight() >= FULLHD.getHeight())) {
435                     boolean yuvSupportFullHD = yuvSizesList.contains(FULLHD) ||
436                             yuvSizesList.contains(FULLHD_ALT);
437                     boolean privateSupportFullHD = privateSizesList.contains(FULLHD) ||
438                             privateSizesList.contains(FULLHD_ALT);
439                     assertTrue("Full device FullHD YUV size not found", yuvSupportFullHD);
440                     assertTrue("Full device FullHD PRIVATE size not found", privateSupportFullHD);
441 
442                     if (isMonochromeWithY8) {
443                         ArrayList<Size> y8SizesList = new ArrayList<>(Arrays.asList(y8Sizes));
444                         boolean y8SupportFullHD = y8SizesList.contains(FULLHD) ||
445                                 y8SizesList.contains(FULLHD_ALT);
446                         assertTrue("Full device FullHD Y8 size not found", y8SupportFullHD);
447                     }
448                 }
449                 // remove all FullHD or FullHD_Alt sizes for the remaining of the test
450                 jpegSizesList.remove(FULLHD);
451                 jpegSizesList.remove(FULLHD_ALT);
452             }
453 
454             // Check all sizes other than FullHD
455             if (hwLevel == LIMITED) {
456                 // Remove all jpeg sizes larger than max video size
457                 ArrayList<Size> toBeRemoved = new ArrayList<>();
458                 for (Size size : jpegSizesList) {
459                     if (size.getWidth() >= maxVideoSize.getWidth() &&
460                             size.getHeight() >= maxVideoSize.getHeight()) {
461                         toBeRemoved.add(size);
462                     }
463                 }
464                 jpegSizesList.removeAll(toBeRemoved);
465             }
466 
467             if (compareHardwareLevel(hwLevel, LEVEL_3) >= 0 || hwLevel == FULL ||
468                     hwLevel == LIMITED) {
469                 if (!yuvSizesList.containsAll(jpegSizesList)) {
470                     for (Size s : jpegSizesList) {
471                         if (!yuvSizesList.contains(s)) {
472                             fail("Size " + s + " not found in YUV format");
473                         }
474                     }
475                 }
476 
477                 if (isMonochromeWithY8) {
478                     ArrayList<Size> y8SizesList = new ArrayList<>(Arrays.asList(y8Sizes));
479                     if (!y8SizesList.containsAll(jpegSizesList)) {
480                         for (Size s : jpegSizesList) {
481                             if (!y8SizesList.contains(s)) {
482                                 fail("Size " + s + " not found in Y8 format");
483                             }
484                         }
485                     }
486                 }
487             }
488 
489             if (!privateSizesList.containsAll(yuvSizesList)) {
490                 for (Size s : yuvSizesList) {
491                     if (!privateSizesList.contains(s)) {
492                         fail("Size " + s + " not found in PRIVATE format");
493                     }
494                 }
495             }
496         }
497     }
498 
499     /**
500      * Check JPEG size overrides for devices claiming S Performance class requirement via
501      * Version.MEDIA_PERFORMANCE_CLASS
502      */
503     @Test
testSPerfClassJpegSizes()504     public void testSPerfClassJpegSizes() throws Exception {
505         final boolean isAtLeastSPerfClass =
506                 (Build.VERSION.MEDIA_PERFORMANCE_CLASS >= Build.VERSION_CODES.S);
507         if (!isAtLeastSPerfClass) {
508             return;
509         }
510 
511         String[] cameraIdsUnderTest = getCameraIdsUnderTest();
512         for (int i = 0; i < cameraIdsUnderTest.length; i++) {
513             testSPerfClassJpegSizesByCamera(cameraIdsUnderTest[i]);
514         }
515     }
516 
517     // Verify primary camera devices's supported JPEG sizes are at least 1080p.
testSPerfClassJpegSizesByCamera(String cameraId)518     private void testSPerfClassJpegSizesByCamera(String cameraId) throws Exception {
519         boolean isPrimaryRear = CameraTestUtils.isPrimaryRearFacingCamera(
520                 mCameraManager, cameraId);
521         boolean isPrimaryFront = CameraTestUtils.isPrimaryFrontFacingCamera(
522                 mCameraManager, cameraId);
523         if (!isPrimaryRear && !isPrimaryFront) {
524             return;
525         }
526 
527         CameraCharacteristics c = mCameraManager.getCameraCharacteristics(cameraId);
528         StaticMetadata staticInfo =
529                 new StaticMetadata(c, StaticMetadata.CheckLevel.ASSERT, mCollector);
530 
531         Size[] jpegSizes = staticInfo.getJpegOutputSizesChecked();
532         assertTrue("Primary cameras must support JPEG formats",
533                 jpegSizes != null && jpegSizes.length > 0);
534         int minEuclidDistSquare = Integer.MAX_VALUE;
535         Size closestJpegSizeToVga = VGA;
536         for (Size jpegSize : jpegSizes) {
537             mCollector.expectTrue(
538                     "Primary camera's JPEG size must be at least 1080p, but is "
539                     + jpegSize, jpegSize.getWidth() * jpegSize.getHeight()
540                         >= FULLHD.getWidth() * FULLHD.getHeight());
541             int widthDist = jpegSize.getWidth() - VGA.getWidth();
542             int heightDist = jpegSize.getHeight() - VGA.getHeight();
543             int euclidDistSquare = widthDist * widthDist + heightDist * heightDist;
544             if (euclidDistSquare < minEuclidDistSquare) {
545                 closestJpegSizeToVga = jpegSize;
546                 minEuclidDistSquare = euclidDistSquare;
547             }
548         }
549 
550         CameraDevice camera = null;
551         ImageReader jpegTarget = null;
552         Image image = null;
553         try {
554             camera = CameraTestUtils.openCamera(mCameraManager, cameraId,
555                     /*listener*/null, mHandler);
556 
557             List<OutputConfiguration> outputConfigs = new ArrayList<>();
558             CameraTestUtils.SimpleImageReaderListener imageListener =
559                     new CameraTestUtils.SimpleImageReaderListener();
560             jpegTarget = CameraTestUtils.makeImageReader(VGA,
561                     ImageFormat.JPEG, 1 /*maxNumImages*/, imageListener, mHandler);
562             Surface jpegSurface = jpegTarget.getSurface();
563             outputConfigs.add(new OutputConfiguration(jpegSurface));
564 
565             // isSessionConfigurationSupported will return true for JPEG sizes smaller
566             // than 1080P, due to framework rouding up to closest supported size.
567             CameraTestUtils.SessionConfigSupport sessionConfigSupport =
568                     CameraTestUtils.isSessionConfigSupported(
569                             camera, mHandler, outputConfigs, /*inputConfig*/ null,
570                             SessionConfiguration.SESSION_REGULAR,
571                             mCameraManager, true/*defaultSupport*/);
572             mCollector.expectTrue("isSessionConfiguration fails with error",
573                     !sessionConfigSupport.error);
574             mCollector.expectTrue("isSessionConfiguration returns false for JPEG < 1080p",
575                     sessionConfigSupport.configSupported);
576 
577             // Session creation for JPEG sizes smaller than 1080p will succeed, and the
578             // result JPEG image dimension is rounded up to closest supported size.
579             CaptureRequest.Builder request =
580                     camera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
581             request.addTarget(jpegSurface);
582 
583             CameraCaptureSession.StateCallback sessionListener =
584                     mock(CameraCaptureSession.StateCallback.class);
585             CameraCaptureSession session = CameraTestUtils.configureCameraSessionWithConfig(
586                     camera, outputConfigs, sessionListener, mHandler);
587 
588             verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce())
589                     .onConfigured(any(CameraCaptureSession.class));
590             verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce())
591                     .onReady(any(CameraCaptureSession.class));
592             verify(sessionListener, never()).onConfigureFailed(any(CameraCaptureSession.class));
593             verify(sessionListener, never()).onActive(any(CameraCaptureSession.class));
594             verify(sessionListener, never()).onClosed(any(CameraCaptureSession.class));
595 
596             CameraCaptureSession.CaptureCallback captureListener =
597                     mock(CameraCaptureSession.CaptureCallback.class);
598             session.capture(request.build(), captureListener, mHandler);
599 
600             verify(captureListener, timeout(CAPTURE_TIMEOUT).atLeastOnce())
601                     .onCaptureCompleted(any(CameraCaptureSession.class),
602                             any(CaptureRequest.class), any(TotalCaptureResult.class));
603             verify(captureListener, never()).onCaptureFailed(any(CameraCaptureSession.class),
604                     any(CaptureRequest.class), any(CaptureFailure.class));
605 
606             image = imageListener.getImage(CAPTURE_TIMEOUT);
607             assertNotNull("Image must be valid", image);
608             assertEquals("Image format isn't JPEG", image.getFormat(), ImageFormat.JPEG);
609 
610             byte[] data = CameraTestUtils.getDataFromImage(image);
611             assertTrue("Invalid image data", data != null && data.length > 0);
612 
613             CameraTestUtils.validateJpegData(data, closestJpegSizeToVga.getWidth(),
614                     closestJpegSizeToVga.getHeight(), null /*filePath*/);
615         } finally {
616             if (camera != null) {
617                 camera.close();
618             }
619             if (jpegTarget != null) {
620                 jpegTarget.close();
621             }
622             if (image != null) {
623                 image.close();
624             }
625         }
626     }
627 
verifyCommonRecommendedConfiguration(String id, CameraCharacteristics c, RecommendedStreamConfigurationMap config, boolean checkNoInput, boolean checkNoHighRes, boolean checkNoHighSpeed, boolean checkNoPrivate, boolean checkNoDepth)628     private void verifyCommonRecommendedConfiguration(String id, CameraCharacteristics c,
629             RecommendedStreamConfigurationMap config, boolean checkNoInput,
630             boolean checkNoHighRes, boolean checkNoHighSpeed, boolean checkNoPrivate,
631             boolean checkNoDepth) {
632         StreamConfigurationMap fullConfig = c.get(
633                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
634         assertNotNull(String.format("No stream configuration map found for ID: %s!", id),
635                 fullConfig);
636 
637         Set<Integer> recommendedOutputFormats = config.getOutputFormats();
638 
639         if (checkNoInput) {
640             Set<Integer> inputFormats = config.getInputFormats();
641             assertTrue(String.format("Recommended configuration must not include any input " +
642                     "streams for ID: %s", id),
643                     ((inputFormats == null) || (inputFormats.size() == 0)));
644         }
645 
646         if (checkNoHighRes) {
647             for (int format : recommendedOutputFormats) {
648                 Set<Size> highResSizes = config.getHighResolutionOutputSizes(format);
649                 assertTrue(String.format("Recommended configuration should not include any " +
650                         "high resolution sizes, which cannot operate at full " +
651                         "BURST_CAPTURE rate for ID: %s", id),
652                         ((highResSizes == null) || (highResSizes.size() == 0)));
653             }
654         }
655 
656         if (checkNoHighSpeed) {
657             Set<Size> highSpeedSizes = config.getHighSpeedVideoSizes();
658             assertTrue(String.format("Recommended configuration must not include any high " +
659                     "speed configurations for ID: %s", id),
660                     ((highSpeedSizes == null) || (highSpeedSizes.size() == 0)));
661         }
662 
663         int[] exhaustiveOutputFormats = fullConfig.getOutputFormats();
664         for (Integer formatInteger : recommendedOutputFormats) {
665             int format = formatInteger.intValue();
666             assertArrayContains(String.format("Unsupported recommended output format: %d for " +
667                     "ID: %s ", format, id), exhaustiveOutputFormats, format);
668             Set<Size> recommendedSizes = config.getOutputSizes(format);
669 
670             switch (format) {
671                 case ImageFormat.PRIVATE:
672                     if (checkNoPrivate) {
673                         fail(String.format("Recommended configuration must not include " +
674                                 "PRIVATE format entries for ID: %s", id));
675                     }
676 
677                     Set<Size> classOutputSizes = config.getOutputSizes(ImageReader.class);
678                     assertCollectionContainsAnyOf(String.format("Recommended output sizes for " +
679                             "ImageReader class don't match the output sizes for the " +
680                             "corresponding format for ID: %s", id), classOutputSizes,
681                             recommendedSizes);
682                     break;
683                 case ImageFormat.DEPTH16:
684                 case ImageFormat.DEPTH_POINT_CLOUD:
685                     if (checkNoDepth) {
686                         fail(String.format("Recommended configuration must not include any DEPTH " +
687                                 "formats for ID: %s", id));
688                     }
689                     break;
690                 default:
691             }
692             Size [] exhaustiveSizes = fullConfig.getOutputSizes(format);
693             for (Size sz : recommendedSizes) {
694                 assertArrayContains(String.format("Unsupported recommended size %s for " +
695                         "format: %d for ID: %s", sz.toString(), format, id),
696                         exhaustiveSizes, sz);
697 
698                 long recommendedMinDuration = config.getOutputMinFrameDuration(format, sz);
699                 long availableMinDuration = fullConfig.getOutputMinFrameDuration(format, sz);
700                 assertTrue(String.format("Recommended minimum frame duration %d for size " +
701                         "%s format: %d doesn't match with currently available minimum" +
702                         " frame duration of %d for ID: %s", recommendedMinDuration,
703                         sz.toString(), format, availableMinDuration, id),
704                         (recommendedMinDuration == availableMinDuration));
705                 long recommendedStallDuration = config.getOutputStallDuration(format, sz);
706                 long availableStallDuration = fullConfig.getOutputStallDuration(format, sz);
707                 assertTrue(String.format("Recommended stall duration %d for size %s" +
708                         " format: %d doesn't match with currently available stall " +
709                         "duration of %d for ID: %s", recommendedStallDuration,
710                         sz.toString(), format, availableStallDuration, id),
711                         (recommendedStallDuration == availableStallDuration));
712 
713                 ImageReader reader = ImageReader.newInstance(sz.getWidth(), sz.getHeight(), format,
714                         /*maxImages*/1);
715                 Surface readerSurface = reader.getSurface();
716                 assertTrue(String.format("ImageReader surface using format %d and size %s is not" +
717                         " supported for ID: %s", format, sz.toString(), id),
718                         config.isOutputSupportedFor(readerSurface));
719                 if (format == ImageFormat.PRIVATE) {
720                     long classMinDuration = config.getOutputMinFrameDuration(ImageReader.class, sz);
721                     assertTrue(String.format("Recommended minimum frame duration %d for size " +
722                             "%s format: %d doesn't match with the duration %d for " +
723                             "ImageReader class of the same size", recommendedMinDuration,
724                             sz.toString(), format, classMinDuration),
725                             classMinDuration == recommendedMinDuration);
726                     long classStallDuration = config.getOutputStallDuration(ImageReader.class, sz);
727                     assertTrue(String.format("Recommended stall duration %d for size " +
728                             "%s format: %d doesn't match with the stall duration %d for " +
729                             "ImageReader class of the same size", recommendedStallDuration,
730                             sz.toString(), format, classStallDuration),
731                             classStallDuration == recommendedStallDuration);
732                 }
733             }
734         }
735     }
736 
verifyRecommendedPreviewConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap previewConfig)737     private void verifyRecommendedPreviewConfiguration(String cameraId, CameraCharacteristics c,
738             RecommendedStreamConfigurationMap previewConfig) {
739         verifyCommonRecommendedConfiguration(cameraId, c, previewConfig, /*checkNoInput*/ true,
740                 /*checkNoHighRes*/ true, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/ false,
741                 /*checkNoDepth*/ true);
742 
743         Set<Integer> outputFormats = previewConfig.getOutputFormats();
744         assertTrue(String.format("No valid YUV_420_888 and PRIVATE preview " +
745                 "formats found in recommended preview configuration for ID: %s", cameraId),
746                 outputFormats.containsAll(Arrays.asList(new Integer(ImageFormat.YUV_420_888),
747                         new Integer(ImageFormat.PRIVATE))));
748     }
749 
verifyRecommendedVideoConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap videoConfig)750     private void verifyRecommendedVideoConfiguration(String cameraId, CameraCharacteristics c,
751             RecommendedStreamConfigurationMap videoConfig) {
752         verifyCommonRecommendedConfiguration(cameraId, c, videoConfig, /*checkNoInput*/ true,
753                 /*checkNoHighRes*/ true, /*checkNoHighSpeed*/ false, /*checkNoPrivate*/false,
754                 /*checkNoDepth*/ true);
755 
756         Set<Size> highSpeedSizes = videoConfig.getHighSpeedVideoSizes();
757         StreamConfigurationMap fullConfig = c.get(
758                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
759         assertNotNull("No stream configuration map found!", fullConfig);
760         Size [] availableHighSpeedSizes = fullConfig.getHighSpeedVideoSizes();
761         if ((highSpeedSizes != null) && (highSpeedSizes.size() > 0)) {
762             for (Size sz : highSpeedSizes) {
763                 assertArrayContains(String.format("Recommended video configuration includes " +
764                         "unsupported high speed configuration with size %s for ID: %s",
765                         sz.toString(), cameraId), availableHighSpeedSizes, sz);
766                 Set<Range<Integer>>  highSpeedFpsRanges =
767                     videoConfig.getHighSpeedVideoFpsRangesFor(sz);
768                 Range<Integer> [] availableHighSpeedFpsRanges =
769                     fullConfig.getHighSpeedVideoFpsRangesFor(sz);
770                 for (Range<Integer> fpsRange : highSpeedFpsRanges) {
771                     assertArrayContains(String.format("Recommended video configuration includes " +
772                             "unsupported high speed fps range [%d %d] for ID: %s",
773                             fpsRange.getLower().intValue(), fpsRange.getUpper().intValue(),
774                             cameraId), availableHighSpeedFpsRanges, fpsRange);
775                 }
776             }
777         }
778 
779         final int[] profileList = {
780             CamcorderProfile.QUALITY_2160P,
781             CamcorderProfile.QUALITY_1080P,
782             CamcorderProfile.QUALITY_480P,
783             CamcorderProfile.QUALITY_720P,
784             CamcorderProfile.QUALITY_CIF,
785             CamcorderProfile.QUALITY_HIGH,
786             CamcorderProfile.QUALITY_LOW,
787             CamcorderProfile.QUALITY_QCIF,
788             CamcorderProfile.QUALITY_QVGA,
789         };
790         Set<Size> privateSizeSet = videoConfig.getOutputSizes(ImageFormat.PRIVATE);
791         for (int profile : profileList) {
792             int idx = Integer.valueOf(cameraId);
793             if (CamcorderProfile.hasProfile(idx, profile)) {
794                 CamcorderProfile videoProfile = CamcorderProfile.get(idx, profile);
795                 Size profileSize  = new Size(videoProfile.videoFrameWidth,
796                         videoProfile.videoFrameHeight);
797                 assertCollectionContainsAnyOf(String.format("Recommended video configuration " +
798                         "doesn't include supported video profile size %s with Private format " +
799                         "for ID: %s", profileSize.toString(), cameraId), privateSizeSet,
800                         Arrays.asList(profileSize));
801             }
802         }
803     }
804 
isSizeWithinSensorMargin(Size sz, Size sensorSize)805     private Pair<Boolean, Size> isSizeWithinSensorMargin(Size sz, Size sensorSize) {
806         final float SIZE_ERROR_MARGIN = 0.03f;
807         float croppedWidth = (float)sensorSize.getWidth();
808         float croppedHeight = (float)sensorSize.getHeight();
809         float sensorAspectRatio = (float)sensorSize.getWidth() / (float)sensorSize.getHeight();
810         float maxAspectRatio = (float)sz.getWidth() / (float)sz.getHeight();
811         if (sensorAspectRatio < maxAspectRatio) {
812             croppedHeight = (float)sensorSize.getWidth() / maxAspectRatio;
813         } else if (sensorAspectRatio > maxAspectRatio) {
814             croppedWidth = (float)sensorSize.getHeight() * maxAspectRatio;
815         }
816         Size croppedSensorSize = new Size((int)croppedWidth, (int)croppedHeight);
817 
818         Boolean match = new Boolean(
819             (sz.getWidth() <= croppedSensorSize.getWidth() * (1.0 + SIZE_ERROR_MARGIN) &&
820              sz.getWidth() >= croppedSensorSize.getWidth() * (1.0 - SIZE_ERROR_MARGIN) &&
821              sz.getHeight() <= croppedSensorSize.getHeight() * (1.0 + SIZE_ERROR_MARGIN) &&
822              sz.getHeight() >= croppedSensorSize.getHeight() * (1.0 - SIZE_ERROR_MARGIN)));
823 
824         return Pair.create(match, croppedSensorSize);
825     }
826 
verifyRecommendedSnapshotConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap snapshotConfig)827     private void verifyRecommendedSnapshotConfiguration(String cameraId, CameraCharacteristics c,
828             RecommendedStreamConfigurationMap snapshotConfig) {
829         verifyCommonRecommendedConfiguration(cameraId, c, snapshotConfig, /*checkNoInput*/ true,
830                 /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/false,
831                 /*checkNoDepth*/ false);
832         Rect activeRect = CameraTestUtils.getValueNotNull(
833                 c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
834         Size arraySize = new Size(activeRect.width(), activeRect.height());
835 
836 
837         ArraySet<Size> snapshotSizeSet = new ArraySet<>(snapshotConfig.getOutputSizes(
838                     ImageFormat.JPEG));
839         Set<Size> highResSnapshotSizeSet = snapshotConfig.getHighResolutionOutputSizes(
840                 ImageFormat.JPEG);
841         if (highResSnapshotSizeSet != null) {
842             snapshotSizeSet.addAll(highResSnapshotSizeSet);
843         }
844         Size[] snapshotSizes = new Size[snapshotSizeSet.size()];
845         snapshotSizes = snapshotSizeSet.toArray(snapshotSizes);
846         Size maxJpegSize = CameraTestUtils.getMaxSize(snapshotSizes);
847         assertTrue(String.format("Maximum recommended Jpeg size %s should be within 3 percent " +
848                 "of the area of the advertised array size %s for ID: %s",
849                 maxJpegSize.toString(), arraySize.toString(), cameraId),
850                 isSizeWithinSensorMargin(maxJpegSize, arraySize).first.booleanValue());
851     }
852 
verifyRecommendedVideoSnapshotConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap videoSnapshotConfig, RecommendedStreamConfigurationMap videoConfig)853     private void verifyRecommendedVideoSnapshotConfiguration(String cameraId,
854             CameraCharacteristics c,
855             RecommendedStreamConfigurationMap videoSnapshotConfig,
856             RecommendedStreamConfigurationMap videoConfig) {
857         verifyCommonRecommendedConfiguration(cameraId, c, videoSnapshotConfig,
858                 /*checkNoInput*/ true, /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true,
859                 /*checkNoPrivate*/ true, /*checkNoDepth*/ true);
860 
861         Set<Integer> outputFormats = videoSnapshotConfig.getOutputFormats();
862         assertCollectionContainsAnyOf(String.format("No valid JPEG format found " +
863                 "in recommended video snapshot configuration for ID: %s", cameraId),
864                 outputFormats, Arrays.asList(new Integer(ImageFormat.JPEG)));
865         assertTrue(String.format("Recommended video snapshot configuration must only advertise " +
866                 "JPEG format for ID: %s", cameraId), outputFormats.size() == 1);
867 
868         Set<Size> privateVideoSizeSet = videoConfig.getOutputSizes(ImageFormat.PRIVATE);
869         Size[] privateVideoSizes = new Size[privateVideoSizeSet.size()];
870         privateVideoSizes = privateVideoSizeSet.toArray(privateVideoSizes);
871         Size maxVideoSize = CameraTestUtils.getMaxSize(privateVideoSizes);
872         Set<Size> outputSizes = videoSnapshotConfig.getOutputSizes(ImageFormat.JPEG);
873         assertCollectionContainsAnyOf(String.format("The maximum recommended video size %s " +
874                 "should be present in the recommended video snapshot configurations for ID: %s",
875                 maxVideoSize.toString(), cameraId), outputSizes, Arrays.asList(maxVideoSize));
876     }
877 
verifyRecommendedRawConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap rawConfig)878     private void verifyRecommendedRawConfiguration(String cameraId,
879             CameraCharacteristics c, RecommendedStreamConfigurationMap rawConfig) {
880         verifyCommonRecommendedConfiguration(cameraId, c, rawConfig, /*checkNoInput*/ true,
881                 /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/ true,
882                 /*checkNoDepth*/ true);
883 
884         Set<Integer> outputFormats = rawConfig.getOutputFormats();
885         for (Integer outputFormatInteger : outputFormats) {
886             int outputFormat = outputFormatInteger.intValue();
887             switch (outputFormat) {
888                 case ImageFormat.RAW10:
889                 case ImageFormat.RAW12:
890                 case ImageFormat.RAW_PRIVATE:
891                 case ImageFormat.RAW_SENSOR:
892                     break;
893                 default:
894                     fail(String.format("Recommended raw configuration map must not contain " +
895                             " non-RAW formats like: %d for ID: %s", outputFormat, cameraId));
896 
897             }
898         }
899     }
900 
verifyRecommendedZSLConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap zslConfig)901     private void verifyRecommendedZSLConfiguration(String cameraId, CameraCharacteristics c,
902             RecommendedStreamConfigurationMap zslConfig) {
903         verifyCommonRecommendedConfiguration(cameraId, c, zslConfig, /*checkNoInput*/ false,
904                 /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/ false,
905                 /*checkNoDepth*/ false);
906 
907         StreamConfigurationMap fullConfig =
908             c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
909         assertNotNull(String.format("No stream configuration map found for ID: %s!", cameraId),
910                 fullConfig);
911         Set<Integer> inputFormats = zslConfig.getInputFormats();
912         int [] availableInputFormats = fullConfig.getInputFormats();
913         for (Integer inputFormatInteger : inputFormats) {
914             int inputFormat = inputFormatInteger.intValue();
915             assertArrayContains(String.format("Recommended ZSL configuration includes " +
916                     "unsupported input format %d for ID: %s", inputFormat, cameraId),
917                     availableInputFormats, inputFormat);
918 
919             Set<Size> inputSizes = zslConfig.getInputSizes(inputFormat);
920             Size [] availableInputSizes = fullConfig.getInputSizes(inputFormat);
921             assertTrue(String.format("Recommended ZSL configuration input format %d includes " +
922                     "invalid input sizes for ID: %s", inputFormat, cameraId),
923                     ((inputSizes != null) && (inputSizes.size() > 0)));
924             for (Size inputSize : inputSizes) {
925                 assertArrayContains(String.format("Recommended ZSL configuration includes " +
926                         "unsupported input format %d with size %s ID: %s", inputFormat,
927                         inputSize.toString(), cameraId), availableInputSizes, inputSize);
928             }
929             Set<Integer> validOutputFormats = zslConfig.getValidOutputFormatsForInput(inputFormat);
930             int [] availableValidOutputFormats = fullConfig.getValidOutputFormatsForInput(
931                     inputFormat);
932             for (Integer outputFormatInteger : validOutputFormats) {
933                 int outputFormat = outputFormatInteger.intValue();
934                 assertArrayContains(String.format("Recommended ZSL configuration includes " +
935                         "unsupported output format %d for input %s ID: %s", outputFormat,
936                         inputFormat, cameraId), availableValidOutputFormats, outputFormat);
937             }
938         }
939     }
940 
checkFormatLatency(int format, long latencyThresholdMs, RecommendedStreamConfigurationMap configMap)941     private void checkFormatLatency(int format, long latencyThresholdMs,
942             RecommendedStreamConfigurationMap configMap) throws Exception {
943         Set<Size> availableSizes = configMap.getOutputSizes(format);
944         assertNotNull(String.format("No available sizes for output format: %d", format),
945                 availableSizes);
946 
947         ImageReader previewReader = null;
948         long threshold = (long) (latencyThresholdMs * LATENCY_TOLERANCE_FACTOR);
949         // for each resolution, check that the end-to-end latency doesn't exceed the given threshold
950         for (Size sz : availableSizes) {
951             try {
952                 // Create ImageReaders, capture session and requests
953                 final ImageReader.OnImageAvailableListener mockListener = mock(
954                         ImageReader.OnImageAvailableListener.class);
955                 createDefaultImageReader(sz, format, MAX_NUM_IMAGES, mockListener);
956                 Size previewSize = mOrderedPreviewSizes.get(0);
957                 previewReader = createImageReader(previewSize, ImageFormat.YUV_420_888,
958                         MAX_NUM_IMAGES, new CameraTestUtils.ImageDropperListener());
959                 Surface previewSurface = previewReader.getSurface();
960                 List<Surface> surfaces = new ArrayList<Surface>();
961                 surfaces.add(previewSurface);
962                 surfaces.add(mReaderSurface);
963                 createSession(surfaces);
964                 CaptureRequest.Builder captureBuilder =
965                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
966                 captureBuilder.addTarget(previewSurface);
967                 CaptureRequest request = captureBuilder.build();
968 
969                 // Let preview run for a while
970                 startCapture(request, /*repeating*/ true, new SimpleCaptureCallback(), mHandler);
971                 Thread.sleep(PREVIEW_RUN_MS);
972 
973                 // Start capture.
974                 captureBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
975                 captureBuilder.addTarget(mReaderSurface);
976                 request = captureBuilder.build();
977 
978                 for (int i = 0; i < MAX_NUM_IMAGES; i++) {
979                     startCapture(request, /*repeating*/ false, new SimpleCaptureCallback(),
980                             mHandler);
981                     verify(mockListener, timeout(threshold).times(1)).onImageAvailable(
982                             any(ImageReader.class));
983                     reset(mockListener);
984                 }
985 
986                 // stop capture.
987                 stopCapture(/*fast*/ false);
988             } finally {
989                 closeDefaultImageReader();
990 
991                 if (previewReader != null) {
992                     previewReader.close();
993                     previewReader = null;
994                 }
995             }
996 
997         }
998     }
999 
verifyRecommendedLowLatencyConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap lowLatencyConfig)1000     private void verifyRecommendedLowLatencyConfiguration(String cameraId, CameraCharacteristics c,
1001             RecommendedStreamConfigurationMap lowLatencyConfig) throws Exception {
1002         verifyCommonRecommendedConfiguration(cameraId, c, lowLatencyConfig, /*checkNoInput*/ true,
1003                 /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/ false,
1004                 /*checkNoDepth*/ true);
1005 
1006         try {
1007             openDevice(cameraId);
1008 
1009             Set<Integer> formats = lowLatencyConfig.getOutputFormats();
1010             for (Integer format : formats) {
1011                 checkFormatLatency(format.intValue(), LOW_LATENCY_THRESHOLD_MS, lowLatencyConfig);
1012             }
1013         } finally {
1014             closeDevice(cameraId);
1015         }
1016 
1017     }
1018 
1019     @Test
testRecommendedStreamConfigurations()1020     public void testRecommendedStreamConfigurations() throws Exception {
1021         String[] allCameraIds = getAllCameraIds();
1022         for (int i = 0; i < allCameraIds.length; i++) {
1023             CameraCharacteristics c = mCharacteristics.get(i);
1024             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1025             assertNotNull("android.request.availableCapabilities must never be null",
1026                     actualCapabilities);
1027 
1028             if (!arrayContains(actualCapabilities, BC)) {
1029                 Log.i(TAG, "Camera " + allCameraIds[i] +
1030                         ": BACKWARD_COMPATIBLE capability not supported, skipping test");
1031                 continue;
1032             }
1033 
1034             try {
1035                 RecommendedStreamConfigurationMap map = c.getRecommendedStreamConfigurationMap(
1036                         RecommendedStreamConfigurationMap.USECASE_PREVIEW - 1);
1037                 fail("Recommended configuration map shouldn't be available for invalid " +
1038                         "use case!");
1039             } catch (IllegalArgumentException e) {
1040                 //Expected continue
1041             }
1042 
1043             try {
1044                 RecommendedStreamConfigurationMap map = c.getRecommendedStreamConfigurationMap(
1045                         RecommendedStreamConfigurationMap.USECASE_10BIT_OUTPUT + 1);
1046                 fail("Recommended configuration map shouldn't be available for invalid " +
1047                         "use case!");
1048             } catch (IllegalArgumentException e) {
1049                 //Expected continue
1050             }
1051 
1052             RecommendedStreamConfigurationMap previewConfig =
1053                     c.getRecommendedStreamConfigurationMap(
1054                     RecommendedStreamConfigurationMap.USECASE_PREVIEW);
1055             RecommendedStreamConfigurationMap videoRecordingConfig =
1056                     c.getRecommendedStreamConfigurationMap(
1057                     RecommendedStreamConfigurationMap.USECASE_RECORD);
1058             RecommendedStreamConfigurationMap videoSnapshotConfig =
1059                     c.getRecommendedStreamConfigurationMap(
1060                     RecommendedStreamConfigurationMap.USECASE_VIDEO_SNAPSHOT);
1061             RecommendedStreamConfigurationMap snapshotConfig =
1062                     c.getRecommendedStreamConfigurationMap(
1063                     RecommendedStreamConfigurationMap.USECASE_SNAPSHOT);
1064             RecommendedStreamConfigurationMap rawConfig =
1065                     c.getRecommendedStreamConfigurationMap(
1066                     RecommendedStreamConfigurationMap.USECASE_RAW);
1067             RecommendedStreamConfigurationMap zslConfig =
1068                     c.getRecommendedStreamConfigurationMap(
1069                     RecommendedStreamConfigurationMap.USECASE_ZSL);
1070             RecommendedStreamConfigurationMap lowLatencyConfig =
1071                     c.getRecommendedStreamConfigurationMap(
1072                     RecommendedStreamConfigurationMap.USECASE_LOW_LATENCY_SNAPSHOT);
1073             RecommendedStreamConfigurationMap dynamic10BitOutputConfig =
1074                     c.getRecommendedStreamConfigurationMap(
1075                             RecommendedStreamConfigurationMap.USECASE_10BIT_OUTPUT);
1076             if ((previewConfig == null) && (videoRecordingConfig == null) &&
1077                     (videoSnapshotConfig == null) && (snapshotConfig == null) &&
1078                     (rawConfig == null) && (zslConfig == null) && (lowLatencyConfig == null)) {
1079                 Log.i(TAG, "Camera " + allCameraIds[i] +
1080                         " doesn't support recommended configurations, skipping test");
1081                 continue;
1082             }
1083 
1084             assertNotNull(String.format("Mandatory recommended preview configuration map not " +
1085                     "found for: ID %s", allCameraIds[i]), previewConfig);
1086             verifyRecommendedPreviewConfiguration(allCameraIds[i], c, previewConfig);
1087 
1088             assertNotNull(String.format("Mandatory recommended video recording configuration map " +
1089                     "not found for: ID %s", allCameraIds[i]), videoRecordingConfig);
1090             verifyRecommendedVideoConfiguration(allCameraIds[i], c, videoRecordingConfig);
1091 
1092             assertNotNull(String.format("Mandatory recommended video snapshot configuration map " +
1093                     "not found for: ID %s", allCameraIds[i]), videoSnapshotConfig);
1094             verifyRecommendedVideoSnapshotConfiguration(allCameraIds[i], c, videoSnapshotConfig,
1095                     videoRecordingConfig);
1096 
1097             assertNotNull(String.format("Mandatory recommended snapshot configuration map not " +
1098                     "found for: ID %s", allCameraIds[i]), snapshotConfig);
1099             verifyRecommendedSnapshotConfiguration(allCameraIds[i], c, snapshotConfig);
1100 
1101             if (arrayContains(actualCapabilities, RAW)) {
1102                 assertNotNull(String.format("Mandatory recommended raw configuration map not " +
1103                         "found for: ID %s", allCameraIds[i]), rawConfig);
1104                 verifyRecommendedRawConfiguration(allCameraIds[i], c, rawConfig);
1105             }
1106 
1107             if (arrayContains(actualCapabilities, OPAQUE_REPROCESS) ||
1108                     arrayContains(actualCapabilities, YUV_REPROCESS)) {
1109                 assertNotNull(String.format("Mandatory recommended ZSL configuration map not " +
1110                         "found for: ID %s", allCameraIds[i]), zslConfig);
1111                 verifyRecommendedZSLConfiguration(allCameraIds[i], c, zslConfig);
1112             }
1113 
1114             if (lowLatencyConfig != null) {
1115                 verifyRecommendedLowLatencyConfiguration(allCameraIds[i], c, lowLatencyConfig);
1116             }
1117 
1118             if (dynamic10BitOutputConfig != null) {
1119                 verifyCommonRecommendedConfiguration(allCameraIds[i], c, dynamic10BitOutputConfig,
1120                         /*checkNoInput*/ true, /*checkNoHighRes*/ false,
1121                         /*checkNoHighSpeed*/ false, /*checkNoPrivate*/ false,
1122                         /*checkNoDepth*/ true);
1123             }
1124         }
1125     }
1126 
1127     /**
1128      * Test {@link CameraCharacteristics#getKeys}
1129      */
1130     @Test
testKeys()1131     public void testKeys() throws Exception {
1132         String[] allCameraIds = getAllCameraIds();
1133         for (int i = 0; i < allCameraIds.length; i++) {
1134             CameraCharacteristics c = mCharacteristics.get(i);
1135             mCollector.setCameraId(allCameraIds[i]);
1136 
1137             if (VERBOSE) {
1138                 Log.v(TAG, "testKeys - testing characteristics for camera " + allCameraIds[i]);
1139             }
1140 
1141             List<CameraCharacteristics.Key<?>> allKeys = c.getKeys();
1142             assertNotNull("Camera characteristics keys must not be null", allKeys);
1143             assertFalse("Camera characteristics keys must have at least 1 key",
1144                     allKeys.isEmpty());
1145 
1146             for (CameraCharacteristics.Key<?> key : allKeys) {
1147                 assertKeyPrefixValid(key.getName());
1148 
1149                 // All characteristics keys listed must never be null
1150                 mCollector.expectKeyValueNotNull(c, key);
1151 
1152                 // TODO: add a check that key must not be @hide
1153             }
1154 
1155             /*
1156              * List of keys that must be present in camera characteristics (not null).
1157              *
1158              * Keys for LIMITED, FULL devices might be available despite lacking either
1159              * the hardware level or the capability. This is *OK*. This only lists the
1160              * *minimal* requirements for a key to be listed.
1161              *
1162              * LEGACY devices are a bit special since they map to api1 devices, so we know
1163              * for a fact most keys are going to be illegal there so they should never be
1164              * available.
1165              *
1166              * For LIMITED-level keys, if the level is >= LIMITED, then the capabilities are used to
1167              * do the actual checking.
1168              */
1169             {
1170                 //                                           (Key Name)                                     (HW Level)  (Capabilities <Var-Arg>)
1171                 expectKeyAvailable(c, CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES     , OPT      ,   BC                   );
1172                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_MODES                         , OPT      ,   BC                   );
1173                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES          , OPT      ,   BC                   );
1174                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES                      , OPT      ,   BC                   );
1175                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES          , OPT      ,   BC                   );
1176                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE                   , OPT      ,   BC                   );
1177                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP                    , OPT      ,   BC                   );
1178                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE                       , OPT      ,   BC                   );
1179                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES                      , OPT      ,   BC                   );
1180                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AUTOFRAMING_AVAILABLE                   , LIMITED  ,   NONE                 );
1181                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS                       , OPT      ,   BC                   );
1182                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES                   , OPT      ,   BC                   );
1183                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES     , OPT      ,   BC                   );
1184                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES                     , OPT      ,   BC                   );
1185                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE                      , OPT      ,   BC                   );
1186                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AE                          , OPT      ,   BC                   );
1187                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AF                          , OPT      ,   BC                   );
1188                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB                         , OPT      ,   BC                   );
1189                 expectKeyAvailable(c, CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES                       , FULL     ,   NONE                 );
1190                 expectKeyAvailable(c, CameraCharacteristics.FLASH_INFO_AVAILABLE                            , OPT      ,   BC                   );
1191                 expectKeyAvailable(c, CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES             , OPT      ,   RAW                  );
1192                 expectKeyAvailable(c, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL                   , OPT      ,   BC                   );
1193                 expectKeyAvailable(c, CameraCharacteristics.INFO_VERSION                                    , OPT      ,   NONE                 );
1194                 expectKeyAvailable(c, CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES                  , OPT      ,   BC                   );
1195                 expectKeyAvailable(c, CameraCharacteristics.LENS_FACING                                     , OPT      ,   BC                   );
1196                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES                   , FULL     ,   MANUAL_SENSOR        );
1197                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES            , FULL     ,   MANUAL_SENSOR        );
1198                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION       , LIMITED  ,   BC                   );
1199                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION            , LIMITED  ,   MANUAL_SENSOR        );
1200                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE                   , LIMITED  ,   BC                   );
1201                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE                , LIMITED  ,   BC                   );
1202                 expectKeyAvailable(c, CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES , OPT      ,   BC                   );
1203                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES                  , OPT      ,   BC                   );
1204                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS                   , OPT      ,   YUV_REPROCESS, OPAQUE_REPROCESS);
1205                 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP                 , OPT      ,   CONSTRAINED_HIGH_SPEED);
1206                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC                     , OPT      ,   BC                   );
1207                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING            , OPT      ,   BC                   );
1208                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW                      , OPT      ,   BC                   );
1209                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT                    , OPT      ,   BC                   );
1210                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH                      , OPT      ,   BC                   );
1211                 expectKeyAvailable(c, CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM               , OPT      ,   BC                   );
1212                 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP                 , OPT      ,   BC                   );
1213                 expectKeyAvailable(c, CameraCharacteristics.SCALER_CROPPING_TYPE                            , OPT      ,   BC                   );
1214                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN                      , FULL     ,   RAW                  );
1215                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE                   , OPT      ,   BC, RAW              );
1216                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT            , FULL     ,   RAW                  );
1217                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE                 , FULL     ,   MANUAL_SENSOR        );
1218                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION                  , FULL     ,   MANUAL_SENSOR        );
1219                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE                    , OPT      ,   BC                   );
1220                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE                   , FULL     ,   MANUAL_SENSOR        );
1221                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL                         , OPT      ,   RAW                  );
1222                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE                    , OPT      ,   BC                   );
1223                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY                   , FULL     ,   MANUAL_SENSOR        );
1224                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_ORIENTATION                              , OPT      ,   BC                   );
1225                 expectKeyAvailable(c, CameraCharacteristics.SHADING_AVAILABLE_MODES                         , LIMITED  ,   MANUAL_POSTPROC, RAW );
1226                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES     , OPT      ,   BC                   );
1227                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES   , OPT      ,   RAW                  );
1228                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES, LIMITED  ,   RAW                  );
1229                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT                  , OPT      ,   BC                   );
1230                 expectKeyAvailable(c, CameraCharacteristics.SYNC_MAX_LATENCY                                , OPT      ,   BC                   );
1231                 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES                , FULL     ,   MANUAL_POSTPROC      );
1232                 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS                        , FULL     ,   MANUAL_POSTPROC      );
1233 
1234                 // Future: Use column editors for modifying above, ignore line length to keep 1 key per line
1235 
1236                 // TODO: check that no other 'android' keys are listed in #getKeys if they aren't in the above list
1237             }
1238 
1239             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1240             assertNotNull("android.request.availableCapabilities must never be null",
1241                     actualCapabilities);
1242             boolean isMonochrome = arrayContains(actualCapabilities,
1243                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME);
1244             if (!isMonochrome) {
1245                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1                   , OPT      ,   RAW                  );
1246                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM1                         , OPT      ,   RAW                  );
1247                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX1                          , OPT      ,   RAW                  );
1248                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1                    , OPT      ,   RAW                  );
1249 
1250 
1251                 // Only check for these if the second reference illuminant is included
1252                 if (allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2)) {
1253                     expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2                    , OPT      ,   RAW                  );
1254                     expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM2                         , OPT      ,   RAW                  );
1255                     expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2                   , OPT      ,   RAW                  );
1256                     expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX2                          , OPT      ,   RAW                  );
1257                 }
1258             }
1259 
1260             // Required key if any of RAW format output is supported
1261             StreamConfigurationMap config =
1262                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1263             assertNotNull(String.format("No stream configuration map found for: ID %s",
1264                     allCameraIds[i]), config);
1265             if (config.isOutputSupportedFor(ImageFormat.RAW_SENSOR) ||
1266                     config.isOutputSupportedFor(ImageFormat.RAW10)  ||
1267                     config.isOutputSupportedFor(ImageFormat.RAW12)  ||
1268                     config.isOutputSupportedFor(ImageFormat.RAW_PRIVATE)) {
1269                 expectKeyAvailable(c,
1270                         CameraCharacteristics.CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE, OPT, BC);
1271             }
1272 
1273             // External Camera exceptional keys
1274             Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
1275             boolean isExternalCamera = (hwLevel ==
1276                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
1277             if (!isExternalCamera) {
1278                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS               , OPT      ,   BC                   );
1279                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES             , OPT      ,   BC                   );
1280                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE                       , OPT      ,   BC                   );
1281             }
1282 
1283 
1284             // Verify version is a short text string.
1285             if (allKeys.contains(CameraCharacteristics.INFO_VERSION)) {
1286                 final String TEXT_REGEX = "[\\p{Alnum}\\p{Punct}\\p{Space}]*";
1287                 final int MAX_VERSION_LENGTH = 256;
1288 
1289                 String version = c.get(CameraCharacteristics.INFO_VERSION);
1290                 mCollector.expectTrue("Version contains non-text characters: " + version,
1291                         version.matches(TEXT_REGEX));
1292                 mCollector.expectLessOrEqual("Version too long: " + version, MAX_VERSION_LENGTH,
1293                         version.length());
1294             }
1295 
1296             // Verify COLOR_CORRECTION_AVAILABLE_MODES is populated if color correction
1297             // mode is supported
1298             if (Flags.colorTemperature()) {
1299                 List<CaptureRequest.Key<?>> availableRequestKeys =
1300                         c.getAvailableCaptureRequestKeys();
1301                 if (availableRequestKeys.contains(CaptureRequest.COLOR_CORRECTION_MODE)) {
1302                     mCollector.expectNotNull("COLOR_CORRECTION_AVAILABLE_MODES must be advertised"
1303                             + " if COLOR_CORRECTION_MODE is supported",
1304                             c.get(CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_MODES));
1305                 }
1306             }
1307         }
1308     }
1309 
1310     /**
1311      * Test values for static metadata used by the RAW capability.
1312      */
1313     @Test
testStaticRawCharacteristics()1314     public void testStaticRawCharacteristics() throws Exception {
1315         String[] allCameraIds = getAllCameraIds();
1316         for (int i = 0; i < allCameraIds.length; i++) {
1317             CameraCharacteristics c = mCharacteristics.get(i);
1318             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1319             assertNotNull("android.request.availableCapabilities must never be null",
1320                     actualCapabilities);
1321             if (!arrayContains(actualCapabilities,
1322                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
1323                 Log.i(TAG, "RAW capability is not supported in camera " + allCameraIds[i] +
1324                         ". Skip the test.");
1325                 continue;
1326             }
1327 
1328             Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
1329             if (actualHwLevel != null && actualHwLevel == FULL) {
1330                 mCollector.expectKeyValueContains(c,
1331                         CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES,
1332                         CameraCharacteristics.HOT_PIXEL_MODE_FAST);
1333             }
1334             mCollector.expectKeyValueContains(c,
1335                     CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES, false);
1336             mCollector.expectKeyValueGreaterThan(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL,
1337                     MIN_ALLOWABLE_WHITELEVEL);
1338 
1339 
1340             boolean isMonochrome = arrayContains(actualCapabilities,
1341                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME);
1342             if (!isMonochrome) {
1343                 mCollector.expectKeyValueIsIn(c,
1344                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
1345                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB,
1346                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG,
1347                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG,
1348                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR);
1349                 // TODO: SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB isn't supported yet.
1350 
1351                 mCollector.expectKeyValueInRange(c,
1352                         CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1,
1353                         CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT,
1354                         CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN);
1355                 // Only check the range if the second reference illuminant is avaliable
1356                 if (c.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2) != null) {
1357                         mCollector.expectKeyValueInRange(c,
1358                         CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2,
1359                         (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT,
1360                         (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN);
1361                 }
1362 
1363                 Rational[] zeroes = new Rational[9];
1364                 Arrays.fill(zeroes, Rational.ZERO);
1365 
1366                 ColorSpaceTransform zeroed = new ColorSpaceTransform(zeroes);
1367                 mCollector.expectNotEquals("Forward Matrix1 should not contain all zeroes.", zeroed,
1368                         c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX1));
1369                 mCollector.expectNotEquals("Forward Matrix2 should not contain all zeroes.", zeroed,
1370                         c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX2));
1371                 mCollector.expectNotEquals("Calibration Transform1 should not contain all zeroes.",
1372                         zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1));
1373                 mCollector.expectNotEquals("Calibration Transform2 should not contain all zeroes.",
1374                         zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2));
1375                 mCollector.expectNotEquals("Color Transform1 should not contain all zeroes.",
1376                         zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1));
1377                 mCollector.expectNotEquals("Color Transform2 should not contain all zeroes.",
1378                         zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2));
1379             } else {
1380                 mCollector.expectKeyValueIsIn(c,
1381                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
1382                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO,
1383                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR);
1384                 // TODO: SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB isn't supported yet.
1385             }
1386 
1387             BlackLevelPattern blackLevel = mCollector.expectKeyValueNotNull(c,
1388                     CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN);
1389             if (blackLevel != null) {
1390                 String blackLevelPatternString = blackLevel.toString();
1391                 if (VERBOSE) {
1392                     Log.v(TAG, "Black level pattern: " + blackLevelPatternString);
1393                 }
1394                 int[] blackLevelPattern = new int[BlackLevelPattern.COUNT];
1395                 blackLevel.copyTo(blackLevelPattern, /*offset*/0);
1396                 if (isMonochrome) {
1397                     for (int index = 1; index < BlackLevelPattern.COUNT; index++) {
1398                         mCollector.expectEquals(
1399                                 "Monochrome camera 2x2 channels blacklevel value must be the same.",
1400                                 blackLevelPattern[index], blackLevelPattern[0]);
1401                     }
1402                 }
1403 
1404                 Integer whitelevel = c.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
1405                 if (whitelevel != null) {
1406                     mCollector.expectValuesInRange("BlackLevelPattern", blackLevelPattern, 0,
1407                             whitelevel);
1408                 } else {
1409                     mCollector.addMessage(
1410                             "No WhiteLevel available, cannot check BlackLevelPattern range.");
1411                 }
1412             }
1413 
1414             // TODO: profileHueSatMap, and profileToneCurve aren't supported yet.
1415         }
1416     }
1417 
1418     /**
1419      * Test values for the available session keys.
1420      */
1421     @Test
testStaticSessionKeys()1422     public void testStaticSessionKeys() throws Exception {
1423         for (CameraCharacteristics c : mCharacteristics) {
1424             List<CaptureRequest.Key<?>> availableSessionKeys = c.getAvailableSessionKeys();
1425             if (availableSessionKeys == null) {
1426                 continue;
1427             }
1428             List<CaptureRequest.Key<?>> availableRequestKeys = c.getAvailableCaptureRequestKeys();
1429 
1430             //Every session key should be part of the available request keys
1431             for (CaptureRequest.Key<?> key : availableSessionKeys) {
1432                 assertTrue("Session key:" + key.getName() + " not present in the available capture "
1433                         + "request keys!", availableRequestKeys.contains(key));
1434             }
1435         }
1436     }
1437 
1438     /**
1439      * Test values for static metadata used by the BURST capability.
1440      */
1441     @Test
testStaticBurstCharacteristics()1442     public void testStaticBurstCharacteristics() throws Exception {
1443         String[] allCameraIds = getAllCameraIds();
1444         for (int i = 0; i < allCameraIds.length; i++) {
1445             CameraCharacteristics c = mCharacteristics.get(i);
1446             int[] actualCapabilities = CameraTestUtils.getValueNotNull(
1447                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1448 
1449             // Check if the burst capability is defined
1450             boolean haveBurstCapability = arrayContains(actualCapabilities,
1451                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
1452             boolean haveBC = arrayContains(actualCapabilities,
1453                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
1454 
1455             if(haveBurstCapability && !haveBC) {
1456                 fail("Must have BACKWARD_COMPATIBLE capability if BURST_CAPTURE capability is defined");
1457             }
1458 
1459             if (!haveBC) continue;
1460 
1461             StreamConfigurationMap config =
1462                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1463             assertNotNull(String.format("No stream configuration map found for: ID %s",
1464                     allCameraIds[i]), config);
1465             Rect activeRect = CameraTestUtils.getValueNotNull(
1466                     c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
1467             Size sensorSize = new Size(activeRect.width(), activeRect.height());
1468 
1469             // Ensure that max YUV size matches max JPEG size
1470             Size maxYuvSize = CameraTestUtils.getMaxSize(
1471                     config.getOutputSizes(ImageFormat.YUV_420_888));
1472             Size maxFastYuvSize = maxYuvSize;
1473 
1474             Size[] slowYuvSizes = config.getHighResolutionOutputSizes(ImageFormat.YUV_420_888);
1475             Size maxSlowYuvSizeLessThan24M = null;
1476             if (haveBurstCapability && slowYuvSizes != null && slowYuvSizes.length > 0) {
1477                 Size maxSlowYuvSize = CameraTestUtils.getMaxSize(slowYuvSizes);
1478                 final int SIZE_24MP_BOUND = 24000000;
1479                 maxSlowYuvSizeLessThan24M =
1480                         CameraTestUtils.getMaxSizeWithBound(slowYuvSizes, SIZE_24MP_BOUND);
1481                 maxYuvSize = CameraTestUtils.getMaxSize(new Size[]{maxYuvSize, maxSlowYuvSize});
1482             }
1483 
1484             Size maxJpegSize = CameraTestUtils.getMaxSize(CameraTestUtils.getSupportedSizeForFormat(
1485                     ImageFormat.JPEG, allCameraIds[i], mCameraManager));
1486 
1487             boolean haveMaxYuv = maxYuvSize != null ?
1488                 (maxJpegSize.getWidth() <= maxYuvSize.getWidth() &&
1489                         maxJpegSize.getHeight() <= maxYuvSize.getHeight()) : false;
1490 
1491             Pair<Boolean, Size> maxYuvMatchSensorPair = isSizeWithinSensorMargin(maxYuvSize,
1492                     sensorSize);
1493 
1494             // No need to do null check since framework will generate the key if HAL don't supply
1495             boolean haveAeLock = CameraTestUtils.getValueNotNull(
1496                     c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE);
1497             boolean haveAwbLock = CameraTestUtils.getValueNotNull(
1498                     c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE);
1499 
1500             // Ensure that some >=8MP YUV output is fast enough - needs to be at least 20 fps
1501 
1502             long maxFastYuvRate =
1503                     config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxFastYuvSize);
1504             final long MIN_8MP_DURATION_BOUND_NS = 50000000; // 50 ms, 20 fps
1505             boolean haveFastYuvRate = maxFastYuvRate <= MIN_8MP_DURATION_BOUND_NS;
1506 
1507             final int SIZE_8MP_BOUND = 8000000;
1508             boolean havefast8MPYuv = (maxFastYuvSize.getWidth() * maxFastYuvSize.getHeight()) >
1509                     SIZE_8MP_BOUND;
1510 
1511             // Ensure that max YUV output smaller than 24MP is fast enough
1512             // - needs to be at least 10 fps
1513             final long MIN_MAXSIZE_DURATION_BOUND_NS = 100000000; // 100 ms, 10 fps
1514             long maxYuvRate = maxFastYuvRate;
1515             if (maxSlowYuvSizeLessThan24M != null) {
1516                 maxYuvRate = config.getOutputMinFrameDuration(
1517                         ImageFormat.YUV_420_888, maxSlowYuvSizeLessThan24M);
1518             }
1519             boolean haveMaxYuvRate = maxYuvRate <= MIN_MAXSIZE_DURATION_BOUND_NS;
1520 
1521             // Ensure that there's an FPS range that's fast enough to capture at above
1522             // minFrameDuration, for full-auto bursts at the fast resolutions
1523             Range[] fpsRanges = CameraTestUtils.getValueNotNull(
1524                     c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
1525             float minYuvFps = 1.f / maxFastYuvRate;
1526 
1527             boolean haveFastAeTargetFps = false;
1528             for (Range<Integer> r : fpsRanges) {
1529                 if (r.getLower() >= minYuvFps) {
1530                     haveFastAeTargetFps = true;
1531                     break;
1532                 }
1533             }
1534 
1535             // Ensure that maximum sync latency is small enough for fast setting changes, even if
1536             // it's not quite per-frame
1537 
1538             Integer maxSyncLatencyValue = c.get(CameraCharacteristics.SYNC_MAX_LATENCY);
1539             assertNotNull(String.format("No sync latency declared for ID %s", allCameraIds[i]),
1540                     maxSyncLatencyValue);
1541 
1542             int maxSyncLatency = maxSyncLatencyValue;
1543             final long MAX_LATENCY_BOUND = 4;
1544             boolean haveFastSyncLatency =
1545                 (maxSyncLatency <= MAX_LATENCY_BOUND) && (maxSyncLatency >= 0);
1546 
1547             if (haveBurstCapability) {
1548                 assertTrue("Must have slow YUV size array when BURST_CAPTURE capability is defined!",
1549                         slowYuvSizes != null);
1550                 assertTrue(
1551                         String.format("BURST-capable camera device %s does not have maximum YUV " +
1552                                 "size that is at least max JPEG size",
1553                                 allCameraIds[i]),
1554                         haveMaxYuv);
1555                 assertTrue(
1556                         String.format("BURST-capable camera device %s max-resolution " +
1557                                 "YUV frame rate is too slow" +
1558                                 "(%d ns min frame duration reported, less than %d ns expected)",
1559                                 allCameraIds[i], maxYuvRate, MIN_MAXSIZE_DURATION_BOUND_NS),
1560                         haveMaxYuvRate);
1561                 assertTrue(
1562                         String.format("BURST-capable camera device %s >= 8MP YUV output " +
1563                                 "frame rate is too slow" +
1564                                 "(%d ns min frame duration reported, less than %d ns expected)",
1565                                 allCameraIds[i], maxYuvRate, MIN_8MP_DURATION_BOUND_NS),
1566                         haveFastYuvRate);
1567                 assertTrue(
1568                         String.format("BURST-capable camera device %s does not list an AE target " +
1569                                 " FPS range with min FPS >= %f, for full-AUTO bursts",
1570                                 allCameraIds[i], minYuvFps),
1571                         haveFastAeTargetFps);
1572                 assertTrue(
1573                         String.format("BURST-capable camera device %s YUV sync latency is too long" +
1574                                 "(%d frames reported, [0, %d] frames expected)",
1575                                 allCameraIds[i], maxSyncLatency, MAX_LATENCY_BOUND),
1576                         haveFastSyncLatency);
1577                 assertTrue(
1578                         String.format("BURST-capable camera device %s max YUV size %s should be" +
1579                                 "close to active array size %s or cropped active array size %s",
1580                                 allCameraIds[i], maxYuvSize.toString(), sensorSize.toString(),
1581                                 maxYuvMatchSensorPair.second.toString()),
1582                         maxYuvMatchSensorPair.first.booleanValue());
1583                 assertTrue(
1584                         String.format("BURST-capable camera device %s does not support AE lock",
1585                                 allCameraIds[i]),
1586                         haveAeLock);
1587                 assertTrue(
1588                         String.format("BURST-capable camera device %s does not support AWB lock",
1589                                 allCameraIds[i]),
1590                         haveAwbLock);
1591             } else {
1592                 assertTrue("Must have null slow YUV size array when no BURST_CAPTURE capability!",
1593                         slowYuvSizes == null);
1594                 assertTrue(
1595                         String.format("Camera device %s has all the requirements for BURST" +
1596                                 " capability but does not report it!", allCameraIds[i]),
1597                         !(haveMaxYuv && haveMaxYuvRate && haveFastYuvRate && haveFastAeTargetFps &&
1598                                 haveFastSyncLatency && maxYuvMatchSensorPair.first.booleanValue() &&
1599                                 haveAeLock && haveAwbLock));
1600             }
1601         }
1602     }
1603 
1604     /**
1605      * Check reprocessing capabilities.
1606      */
1607     @Test
testReprocessingCharacteristics()1608     public void testReprocessingCharacteristics() throws Exception {
1609         String[] allCameraIds = getAllCameraIds();
1610         for (int i = 0; i < allCameraIds.length; i++) {
1611             Log.i(TAG, "testReprocessingCharacteristics: Testing camera ID " + allCameraIds[i]);
1612 
1613             CameraCharacteristics c = mCharacteristics.get(i);
1614             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1615             assertNotNull("android.request.availableCapabilities must never be null",
1616                     capabilities);
1617             boolean supportYUV = arrayContains(capabilities,
1618                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
1619             boolean supportOpaque = arrayContains(capabilities,
1620                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
1621             StreamConfigurationMap configs =
1622                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1623             Integer maxNumInputStreams =
1624                     c.get(CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS);
1625             int[] availableEdgeModes = c.get(CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES);
1626             int[] availableNoiseReductionModes = c.get(
1627                     CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES);
1628 
1629             int[] inputFormats = configs.getInputFormats();
1630             int[] outputFormats = configs.getOutputFormats();
1631             boolean isMonochromeWithY8 = arrayContains(capabilities, MONOCHROME)
1632                     && arrayContains(outputFormats, ImageFormat.Y8);
1633 
1634             boolean supportZslEdgeMode = false;
1635             boolean supportZslNoiseReductionMode = false;
1636             boolean supportHiQNoiseReductionMode = false;
1637             boolean supportHiQEdgeMode = false;
1638 
1639             if (availableEdgeModes != null) {
1640                 supportZslEdgeMode = Arrays.asList(CameraTestUtils.toObject(availableEdgeModes)).
1641                         contains(CaptureRequest.EDGE_MODE_ZERO_SHUTTER_LAG);
1642                 supportHiQEdgeMode = Arrays.asList(CameraTestUtils.toObject(availableEdgeModes)).
1643                         contains(CaptureRequest.EDGE_MODE_HIGH_QUALITY);
1644             }
1645 
1646             if (availableNoiseReductionModes != null) {
1647                 supportZslNoiseReductionMode = Arrays.asList(
1648                         CameraTestUtils.toObject(availableNoiseReductionModes)).contains(
1649                         CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG);
1650                 supportHiQNoiseReductionMode = Arrays.asList(
1651                         CameraTestUtils.toObject(availableNoiseReductionModes)).contains(
1652                         CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
1653             }
1654 
1655             if (supportYUV || supportOpaque) {
1656                 mCollector.expectTrue("Support reprocessing but max number of input stream is " +
1657                         maxNumInputStreams, maxNumInputStreams != null && maxNumInputStreams > 0);
1658                 mCollector.expectTrue("Support reprocessing but EDGE_MODE_ZERO_SHUTTER_LAG is " +
1659                         "not supported", supportZslEdgeMode);
1660                 mCollector.expectTrue("Support reprocessing but " +
1661                         "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is not supported",
1662                         supportZslNoiseReductionMode);
1663 
1664                 // For reprocessing, if we only require OFF and ZSL mode, it will be just like jpeg
1665                 // encoding. We implicitly require FAST to make reprocessing meaningful, which means
1666                 // that we also require HIGH_QUALITY.
1667                 mCollector.expectTrue("Support reprocessing but EDGE_MODE_HIGH_QUALITY is " +
1668                         "not supported", supportHiQEdgeMode);
1669                 mCollector.expectTrue("Support reprocessing but " +
1670                         "NOISE_REDUCTION_MODE_HIGH_QUALITY is not supported",
1671                         supportHiQNoiseReductionMode);
1672 
1673                 // Verify mandatory input formats are supported
1674                 mCollector.expectTrue("YUV_420_888 input must be supported for YUV reprocessing",
1675                         !supportYUV || arrayContains(inputFormats, ImageFormat.YUV_420_888));
1676                 mCollector.expectTrue("Y8 input must be supported for YUV reprocessing on " +
1677                         "MONOCHROME devices with Y8 support", !supportYUV || !isMonochromeWithY8
1678                         || arrayContains(inputFormats, ImageFormat.Y8));
1679                 mCollector.expectTrue("PRIVATE input must be supported for OPAQUE reprocessing",
1680                         !supportOpaque || arrayContains(inputFormats, ImageFormat.PRIVATE));
1681 
1682                 // max capture stall must be reported if one of the reprocessing is supported.
1683                 final int MAX_ALLOWED_STALL_FRAMES = 4;
1684                 Integer maxCaptureStall = c.get(CameraCharacteristics.REPROCESS_MAX_CAPTURE_STALL);
1685                 mCollector.expectTrue("max capture stall must be non-null and no larger than "
1686                         + MAX_ALLOWED_STALL_FRAMES,
1687                         maxCaptureStall != null && maxCaptureStall <= MAX_ALLOWED_STALL_FRAMES);
1688 
1689                 for (int input : inputFormats) {
1690                     // Verify mandatory output formats are supported
1691                     int[] outputFormatsForInput = configs.getValidOutputFormatsForInput(input);
1692                     mCollector.expectTrue(
1693                         "YUV_420_888 output must be supported for reprocessing",
1694                         input == ImageFormat.Y8
1695                         || arrayContains(outputFormatsForInput, ImageFormat.YUV_420_888));
1696                     mCollector.expectTrue(
1697                         "Y8 output must be supported for reprocessing on MONOCHROME devices with"
1698                         + " Y8 support", !isMonochromeWithY8 || input == ImageFormat.YUV_420_888
1699                         || arrayContains(outputFormatsForInput, ImageFormat.Y8));
1700                     mCollector.expectTrue("JPEG output must be supported for reprocessing",
1701                             arrayContains(outputFormatsForInput, ImageFormat.JPEG));
1702 
1703                     // Verify camera can output the reprocess input formats and sizes.
1704                     Size[] inputSizes = configs.getInputSizes(input);
1705                     Size[] outputSizes = configs.getOutputSizes(input);
1706                     Size[] highResOutputSizes = configs.getHighResolutionOutputSizes(input);
1707                     mCollector.expectTrue("no input size supported for format " + input,
1708                             inputSizes.length > 0);
1709                     mCollector.expectTrue("no output size supported for format " + input,
1710                             outputSizes.length > 0);
1711 
1712                     for (Size inputSize : inputSizes) {
1713                         mCollector.expectTrue("Camera must be able to output the supported " +
1714                                 "reprocessing input size",
1715                                 arrayContains(outputSizes, inputSize) ||
1716                                 arrayContains(highResOutputSizes, inputSize));
1717                     }
1718                 }
1719             } else {
1720                 mCollector.expectTrue("Doesn't support reprocessing but report input format: " +
1721                         Arrays.toString(inputFormats), inputFormats.length == 0);
1722                 mCollector.expectTrue("Doesn't support reprocessing but max number of input " +
1723                         "stream is " + maxNumInputStreams,
1724                         maxNumInputStreams == null || maxNumInputStreams == 0);
1725                 mCollector.expectTrue("Doesn't support reprocessing but " +
1726                         "EDGE_MODE_ZERO_SHUTTER_LAG is supported", !supportZslEdgeMode);
1727                 mCollector.expectTrue("Doesn't support reprocessing but " +
1728                         "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is supported",
1729                         !supportZslNoiseReductionMode);
1730             }
1731         }
1732     }
1733 
1734     /**
1735      * Check ultra high resolution sensor characteristics.
1736      */
1737     @Test
testUltraHighResolutionSensorCharacteristics()1738     public void testUltraHighResolutionSensorCharacteristics() throws Exception {
1739         String[] allCameraIds = getAllCameraIds();
1740         for (int i = 0; i < allCameraIds.length; i++) {
1741             CameraCharacteristics c = mCharacteristics.get(i);
1742             String cameraId = allCameraIds[i];
1743             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1744             assertNotNull("android.request.availableCapabilities must never be null",
1745                     capabilities);
1746             boolean isUltraHighResolutionSensor = arrayContains(capabilities,
1747                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR);
1748 
1749             boolean supportsRemosaic = arrayContains(capabilities,
1750                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING);
1751 
1752             List<CaptureRequest.Key<?>> requestKeys = c.getAvailableCaptureRequestKeys();
1753             boolean doesSupportSensorPixelMode =
1754                     requestKeys.contains(CaptureRequest.SENSOR_PIXEL_MODE);
1755 
1756             if (!isUltraHighResolutionSensor && !doesSupportSensorPixelMode) {
1757                 Log.i(TAG, "Camera id " + cameraId + " not ultra high resolution / doesn't" +
1758                       " support sensor pixel mode. Skipping " +
1759                       "testUltraHighResolutionSensorCharacteristics");
1760                 continue;
1761             }
1762 
1763             // Test conditions applicable to both ULTRA_HIGH_RESOLUTION_SENSOR devices and those
1764             // which support SENSOR_PIXEL_MODE.
1765             StreamConfigurationMap configs =
1766                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION);
1767             assertNotNull("Maximum resolution stream configuration map must not be null for ultra" +
1768                     " high resolution sensor camera " + cameraId, configs);
1769 
1770             int[] outputFormats = configs.getOutputFormats();
1771             boolean supportsRawOutput =
1772                     arrayContains(outputFormats, ImageFormat.RAW_SENSOR) ||
1773                     arrayContains(outputFormats, ImageFormat.RAW10) ||
1774                     arrayContains(outputFormats, ImageFormat.RAW_PRIVATE) ||
1775                     arrayContains(outputFormats, ImageFormat.RAW12);
1776 
1777             if (supportsRawOutput) {
1778                 Size binningFactor = c.get(CameraCharacteristics.SENSOR_INFO_BINNING_FACTOR);
1779                 assertTrue("SENSOR_INFO_BINNING_FACTOR must be advertised by a sensor that " +
1780                         " supports ULTRA_HIGH_RESOLUTION_SENSOR / SENSOR_PIXEL_MODE with "
1781                         + " RAW outputs - camera id: " +
1782                         cameraId, binningFactor != null);
1783             }
1784 
1785             if (!isUltraHighResolutionSensor) {
1786                 continue;
1787             }
1788 
1789             // These conditions apply to ULTRA_HIGH_RESOLUTION_SENSOR devices.
1790             assertArrayContains(
1791                     String.format("Ultra high resolution sensor, camera id %s" +
1792                     " must also have the RAW capability", cameraId), capabilities,
1793                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW);
1794 
1795             Size uhrPixelArraySize = CameraTestUtils.getValueNotNull(
1796                 c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION);
1797             long uhrSensorSize = uhrPixelArraySize.getHeight() * uhrPixelArraySize.getWidth();
1798 
1799             assertTrue("ULTRA_HIGH_RESOLUTION_SENSOR pixel array size should be at least " +
1800                     MIN_UHR_SENSOR_RESOLUTION + " pixels, is " + uhrSensorSize + ", for camera id "
1801                     + cameraId, uhrSensorSize >= MIN_UHR_SENSOR_RESOLUTION);
1802 
1803             assertArrayContains(String.format("No max res JPEG image format for ultra high" +
1804                   " resolution sensor: ID %s", cameraId), outputFormats, ImageFormat.JPEG);
1805             assertArrayContains(String.format("No max res YUV_420_88 image format for ultra high" +
1806                   " resolution sensor: ID %s", cameraId), outputFormats, ImageFormat.YUV_420_888);
1807             assertArrayContains(String.format("No max res RAW_SENSOR image format for ultra high" +
1808                   " resolution sensor: ID %s", cameraId), outputFormats, ImageFormat.RAW_SENSOR);
1809 
1810             if (supportsRemosaic) {
1811                 testRemosaicReprocessingCharacteristics(cameraId, c);
1812             }
1813       }
1814 
1815     }
1816     /**
1817      * Check remosaic reprocessing capabilities. Check that ImageFormat.RAW_SENSOR is supported as
1818      * input and output.
1819      */
testRemosaicReprocessingCharacteristics(String cameraId, CameraCharacteristics c)1820     private void testRemosaicReprocessingCharacteristics(String cameraId, CameraCharacteristics c) {
1821         StreamConfigurationMap configs =
1822                 c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION);
1823         Integer maxNumInputStreams =
1824                 c.get(CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS);
1825         int[] inputFormats = configs.getInputFormats();
1826         int[] outputFormats = configs.getOutputFormats();
1827 
1828         mCollector.expectTrue("Support reprocessing but max number of input stream is " +
1829                 maxNumInputStreams, maxNumInputStreams != null && maxNumInputStreams > 0);
1830 
1831         // Verify mandatory input formats are supported
1832         mCollector.expectTrue("RAW_SENSOR input support needed for REMOSAIC reprocessing",
1833                 arrayContains(inputFormats, ImageFormat.RAW_SENSOR));
1834         // max capture stall must be reported if one of the reprocessing is supported.
1835         final int MAX_ALLOWED_STALL_FRAMES = 4;
1836         Integer maxCaptureStall = c.get(CameraCharacteristics.REPROCESS_MAX_CAPTURE_STALL);
1837         mCollector.expectTrue("max capture stall must be non-null and no larger than "
1838                 + MAX_ALLOWED_STALL_FRAMES,
1839                 maxCaptureStall != null && maxCaptureStall <= MAX_ALLOWED_STALL_FRAMES);
1840 
1841         for (int input : inputFormats) {
1842             // Verify mandatory output formats are supported
1843             int[] outputFormatsForInput = configs.getValidOutputFormatsForInput(input);
1844 
1845             // Verify camera can output the reprocess input formats and sizes.
1846             Size[] inputSizes = configs.getInputSizes(input);
1847             Size[] outputSizes = configs.getOutputSizes(input);
1848             Size[] highResOutputSizes = configs.getHighResolutionOutputSizes(input);
1849             mCollector.expectTrue("no input size supported for format " + input,
1850                     inputSizes.length > 0);
1851             mCollector.expectTrue("no output size supported for format " + input,
1852                     outputSizes.length > 0);
1853 
1854             for (Size inputSize : inputSizes) {
1855                 mCollector.expectTrue("Camera must be able to output the supported " +
1856                         "reprocessing input size",
1857                         arrayContains(outputSizes, inputSize) ||
1858                         arrayContains(highResOutputSizes, inputSize));
1859             }
1860         }
1861     }
1862 
1863 
1864     /**
1865      * Check depth output capability
1866      */
1867     @Test
testDepthOutputCharacteristics()1868     public void testDepthOutputCharacteristics() throws Exception {
1869         String[] allCameraIds = getAllCameraIds();
1870         for (int i = 0; i < allCameraIds.length; i++) {
1871             Log.i(TAG, "testDepthOutputCharacteristics: Testing camera ID " + allCameraIds[i]);
1872 
1873             CameraCharacteristics c = mCharacteristics.get(i);
1874             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1875             assertNotNull("android.request.availableCapabilities must never be null",
1876                     capabilities);
1877             boolean supportDepth = arrayContains(capabilities,
1878                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT);
1879             StreamConfigurationMap configs =
1880                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1881 
1882             int[] outputFormats = configs.getOutputFormats();
1883             boolean hasDepth16 = arrayContains(outputFormats, ImageFormat.DEPTH16);
1884 
1885             Boolean depthIsExclusive = c.get(CameraCharacteristics.DEPTH_DEPTH_IS_EXCLUSIVE);
1886 
1887             float[] poseRotation = c.get(CameraCharacteristics.LENS_POSE_ROTATION);
1888             float[] poseTranslation = c.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
1889             Integer poseReference = c.get(CameraCharacteristics.LENS_POSE_REFERENCE);
1890             float[] cameraIntrinsics = c.get(CameraCharacteristics.LENS_INTRINSIC_CALIBRATION);
1891             float[] distortion = getLensDistortion(c);
1892             Size pixelArraySize = c.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
1893             Rect precorrectionArray = c.get(
1894                 CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1895             Rect activeArray = c.get(
1896                 CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
1897             Integer facing = c.get(CameraCharacteristics.LENS_FACING);
1898             float jpegAspectRatioThreshold = .01f;
1899             boolean jpegSizeMatch = false;
1900 
1901             // Verify pre-correction array encloses active array
1902             mCollector.expectTrue("preCorrectionArray [" + precorrectionArray.left + ", " +
1903                     precorrectionArray.top + ", " + precorrectionArray.right + ", " +
1904                     precorrectionArray.bottom + "] does not enclose activeArray[" +
1905                     activeArray.left + ", " + activeArray.top + ", " + activeArray.right +
1906                     ", " + activeArray.bottom,
1907                     precorrectionArray.contains(activeArray.left, activeArray.top) &&
1908                     precorrectionArray.contains(activeArray.right-1, activeArray.bottom-1));
1909 
1910             // Verify pixel array encloses pre-correction array
1911             mCollector.expectTrue("preCorrectionArray [" + precorrectionArray.left + ", " +
1912                     precorrectionArray.top + ", " + precorrectionArray.right + ", " +
1913                     precorrectionArray.bottom + "] isn't enclosed by pixelArray[" +
1914                     pixelArraySize.getWidth() + ", " + pixelArraySize.getHeight() + "]",
1915                     precorrectionArray.left >= 0 &&
1916                     precorrectionArray.left < pixelArraySize.getWidth() &&
1917                     precorrectionArray.right > 0 &&
1918                     precorrectionArray.right <= pixelArraySize.getWidth() &&
1919                     precorrectionArray.top >= 0 &&
1920                     precorrectionArray.top < pixelArraySize.getHeight() &&
1921                     precorrectionArray.bottom > 0 &&
1922                     precorrectionArray.bottom <= pixelArraySize.getHeight());
1923 
1924             if (supportDepth) {
1925                 mCollector.expectTrue("Supports DEPTH_OUTPUT but does not support DEPTH16",
1926                         hasDepth16);
1927                 if (hasDepth16) {
1928                     Size[] depthSizes = configs.getOutputSizes(ImageFormat.DEPTH16);
1929                     Size[] jpegSizes = configs.getOutputSizes(ImageFormat.JPEG);
1930                     mCollector.expectTrue("Supports DEPTH_OUTPUT but no sizes for DEPTH16 supported!",
1931                             depthSizes != null && depthSizes.length > 0);
1932                     if (depthSizes != null) {
1933                         for (Size depthSize : depthSizes) {
1934                             mCollector.expectTrue("All depth16 sizes must be positive",
1935                                     depthSize.getWidth() > 0 && depthSize.getHeight() > 0);
1936                             long minFrameDuration = configs.getOutputMinFrameDuration(
1937                                     ImageFormat.DEPTH16, depthSize);
1938                             mCollector.expectTrue("Non-negative min frame duration for depth size "
1939                                     + depthSize + " expected, got " + minFrameDuration,
1940                                     minFrameDuration >= 0);
1941                             long stallDuration = configs.getOutputStallDuration(
1942                                     ImageFormat.DEPTH16, depthSize);
1943                             mCollector.expectTrue("Non-negative stall duration for depth size "
1944                                     + depthSize + " expected, got " + stallDuration,
1945                                     stallDuration >= 0);
1946                             if ((jpegSizes != null) && (!jpegSizeMatch)) {
1947                                 for (Size jpegSize : jpegSizes) {
1948                                     if (jpegSize.equals(depthSize)) {
1949                                         jpegSizeMatch = true;
1950                                         break;
1951                                     } else {
1952                                         float depthAR = (float) depthSize.getWidth() /
1953                                                 (float) depthSize.getHeight();
1954                                         float jpegAR = (float) jpegSize.getWidth() /
1955                                                 (float) jpegSize.getHeight();
1956                                         if (Math.abs(depthAR - jpegAR) <=
1957                                                 jpegAspectRatioThreshold) {
1958                                             jpegSizeMatch = true;
1959                                             break;
1960                                         }
1961                                     }
1962                                 }
1963                             }
1964                         }
1965                     }
1966                 }
1967                 if (arrayContains(outputFormats, ImageFormat.DEPTH_POINT_CLOUD)) {
1968                     Size[] depthCloudSizes = configs.getOutputSizes(ImageFormat.DEPTH_POINT_CLOUD);
1969                     mCollector.expectTrue("Supports DEPTH_POINT_CLOUD " +
1970                             "but no sizes for DEPTH_POINT_CLOUD supported!",
1971                             depthCloudSizes != null && depthCloudSizes.length > 0);
1972                     if (depthCloudSizes != null) {
1973                         for (Size depthCloudSize : depthCloudSizes) {
1974                             mCollector.expectTrue("All depth point cloud sizes must be nonzero",
1975                                     depthCloudSize.getWidth() > 0);
1976                             mCollector.expectTrue("All depth point cloud sizes must be N x 1",
1977                                     depthCloudSize.getHeight() == 1);
1978                             long minFrameDuration = configs.getOutputMinFrameDuration(
1979                                     ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
1980                             mCollector.expectTrue("Non-negative min frame duration for depth size "
1981                                     + depthCloudSize + " expected, got " + minFrameDuration,
1982                                     minFrameDuration >= 0);
1983                             long stallDuration = configs.getOutputStallDuration(
1984                                     ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
1985                             mCollector.expectTrue("Non-negative stall duration for depth size "
1986                                     + depthCloudSize + " expected, got " + stallDuration,
1987                                     stallDuration >= 0);
1988                         }
1989                     }
1990                 }
1991                 if (arrayContains(outputFormats, ImageFormat.DEPTH_JPEG)) {
1992                     mCollector.expectTrue("Supports DEPTH_JPEG but has no DEPTH16 support!",
1993                             hasDepth16);
1994                     mCollector.expectTrue("Supports DEPTH_JPEG but DEPTH_IS_EXCLUSIVE is not " +
1995                             "defined", depthIsExclusive != null);
1996                     mCollector.expectTrue("Supports DEPTH_JPEG but DEPTH_IS_EXCLUSIVE is true",
1997                             !depthIsExclusive.booleanValue());
1998                     Size[] depthJpegSizes = configs.getOutputSizes(ImageFormat.DEPTH_JPEG);
1999                     mCollector.expectTrue("Supports DEPTH_JPEG " +
2000                             "but no sizes for DEPTH_JPEG supported!",
2001                             depthJpegSizes != null && depthJpegSizes.length > 0);
2002                     mCollector.expectTrue("Supports DEPTH_JPEG but there are no JPEG sizes with" +
2003                             " matching DEPTH16 aspect ratio", jpegSizeMatch);
2004                     if (depthJpegSizes != null) {
2005                         for (Size depthJpegSize : depthJpegSizes) {
2006                             mCollector.expectTrue("All depth jpeg sizes must be nonzero",
2007                                     depthJpegSize.getWidth() > 0 && depthJpegSize.getHeight() > 0);
2008                             long minFrameDuration = configs.getOutputMinFrameDuration(
2009                                     ImageFormat.DEPTH_JPEG, depthJpegSize);
2010                             mCollector.expectTrue("Non-negative min frame duration for depth jpeg" +
2011                                    " size " + depthJpegSize + " expected, got " + minFrameDuration,
2012                                     minFrameDuration >= 0);
2013                             long stallDuration = configs.getOutputStallDuration(
2014                                     ImageFormat.DEPTH_JPEG, depthJpegSize);
2015                             mCollector.expectTrue("Non-negative stall duration for depth jpeg size "
2016                                     + depthJpegSize + " expected, got " + stallDuration,
2017                                     stallDuration >= 0);
2018                         }
2019                     }
2020                 } else {
2021                     boolean canSupportDynamicDepth = jpegSizeMatch && !depthIsExclusive;
2022                     mCollector.expectTrue("Device must support DEPTH_JPEG, please check whether " +
2023                             "library libdepthphoto.so is part of the device PRODUCT_PACKAGES",
2024                             !canSupportDynamicDepth);
2025                 }
2026 
2027 
2028                 mCollector.expectTrue("Supports DEPTH_OUTPUT but DEPTH_IS_EXCLUSIVE is not defined",
2029                         depthIsExclusive != null);
2030 
2031                 verifyLensCalibration(poseRotation, poseTranslation, poseReference,
2032                         cameraIntrinsics, distortion, precorrectionArray, facing);
2033 
2034             } else {
2035                 boolean hasFields =
2036                     hasDepth16 && (poseTranslation != null) &&
2037                     (poseRotation != null) && (cameraIntrinsics != null) &&
2038                     (distortion != null) && (depthIsExclusive != null);
2039 
2040                 mCollector.expectTrue(
2041                         "All necessary depth fields defined, but DEPTH_OUTPUT capability is not listed",
2042                         !hasFields);
2043 
2044                 boolean reportCalibration = poseTranslation != null ||
2045                         poseRotation != null || cameraIntrinsics !=null;
2046                 // Verify calibration keys are co-existing
2047                 if (reportCalibration) {
2048                     mCollector.expectTrue(
2049                             "Calibration keys must be co-existing",
2050                             poseTranslation != null && poseRotation != null &&
2051                             cameraIntrinsics !=null);
2052                 }
2053 
2054                 boolean reportDistortion = distortion != null;
2055                 if (reportDistortion) {
2056                     mCollector.expectTrue(
2057                             "Calibration keys must present where distortion is reported",
2058                             reportCalibration);
2059                 }
2060             }
2061         }
2062     }
2063 
2064     /**
2065      * Check 10-Bit output capability
2066      */
2067     @CddTest(requirement="7.5/C-2-1")
2068     @Test
test10BitOutputCharacteristics()2069     public void test10BitOutputCharacteristics() throws Exception {
2070         String[] allCameraIds = getAllCameraIds();
2071         for (int i = 0; i < allCameraIds.length; i++) {
2072             Log.i(TAG, "test10BitOutputCharacteristics: Testing camera ID " + allCameraIds[i]);
2073 
2074             CameraCharacteristics c = mCharacteristics.get(i);
2075             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
2076             assertNotNull("android.request.availableCapabilities must never be null",
2077                     capabilities);
2078             boolean supports10BitOutput = arrayContains(capabilities,
2079                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT);
2080             if (!supports10BitOutput) {
2081                 continue;
2082             }
2083 
2084             DynamicRangeProfiles dynamicProfiles = c.get(
2085                     CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES);
2086             mCollector.expectNotNull("Dynamic range profile must always be present in case " +
2087                     "of 10-bit capable devices!", dynamicProfiles);
2088             Set<Long> supportedProfiles = dynamicProfiles.getSupportedProfiles();
2089             mCollector.expectTrue("Dynamic range profiles not present!",
2090                     !supportedProfiles.isEmpty());
2091             // STANDARD and HLG10 must always be present in the supported profiles
2092             mCollector.expectContains(supportedProfiles.toArray(), DynamicRangeProfiles.STANDARD);
2093             mCollector.expectContains(supportedProfiles.toArray(), DynamicRangeProfiles.HLG10);
2094 
2095             Long recommendedProfile = c.get(
2096                     CameraCharacteristics.REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE);
2097             assertNotNull(recommendedProfile);
2098             mCollector.expectContains(supportedProfiles.toArray(), recommendedProfile);
2099             mCollector.expectTrue("The recommended 10-bit dynamic range profile must " +
2100                             "not be the same as standard",
2101                     recommendedProfile != DynamicRangeProfiles.STANDARD);
2102             mCollector.expectTrue("HLG10 profile must not have extra latency!",
2103                     !dynamicProfiles.isExtraLatencyPresent(DynamicRangeProfiles.HLG10));
2104             mCollector.expectTrue("STANDARD profile must not have extra latency!",
2105                     !dynamicProfiles.isExtraLatencyPresent(DynamicRangeProfiles.STANDARD));
2106 
2107             // Verify constraints validity. For example if HLG10 advertises support for HDR10, then
2108             // there shouldn't be any HDR10 constraints related to HLG10.
2109             for (Long profile : supportedProfiles) {
2110                 Set<Long> currentConstraints =
2111                         dynamicProfiles.getProfileCaptureRequestConstraints(profile);
2112                 boolean isSameProfilePresent = false;
2113                 for (Long concurrentProfile : currentConstraints) {
2114                     if (Objects.equals(concurrentProfile, profile)) {
2115                         isSameProfilePresent = true;
2116                         continue;
2117                     }
2118                     String msg = String.format("Dynamic profile %d supports profile %d " +
2119                                     "in the same capture request, however profile %d is not" +
2120                                     "advertised as supported!", profile, concurrentProfile,
2121                                     concurrentProfile);
2122                     mCollector.expectTrue(msg, supportedProfiles.contains(concurrentProfile));
2123 
2124                     Set<Long> supportedConstraints =
2125                             dynamicProfiles.getProfileCaptureRequestConstraints(concurrentProfile);
2126                     msg = String.format("Dynamic range profile %d advertises support " +
2127                                     "for profile %d, however the opposite is not true!",
2128                                     profile, concurrentProfile);
2129                     mCollector.expectTrue(msg, supportedConstraints.isEmpty() ||
2130                             supportedConstraints.contains(profile));
2131                 }
2132 
2133                 String msg = String.format("Dynamic profile %d not present in its advertised " +
2134                         "capture request constraints!", profile);
2135                 mCollector.expectTrue(msg, isSameProfilePresent || currentConstraints.isEmpty());
2136             }
2137         }
2138     }
2139 
2140     /**
2141      * If device implementations support HDR 10-bit output capability, then they
2142      * MUST support 10-bit output for either the primary rear-facing or the primary front-facing
2143      * camera.
2144      */
2145     @CddTest(requirement="7.5/C-2-2")
2146     @Test
test10BitDeviceSupport()2147     public void test10BitDeviceSupport() throws Exception {
2148         boolean rearFacing10bitSupport = false;
2149         boolean frontFacing10bitSupport = false;
2150         boolean device10bitSupport = false;
2151 
2152         String[] allCameraIds = getAllCameraIds();
2153         for (int i = 0; i < allCameraIds.length; i++) {
2154             Log.i(TAG, "test10BitDeviceSupport: Testing camera ID " + allCameraIds[i]);
2155 
2156             CameraCharacteristics c = mCharacteristics.get(i);
2157             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
2158             assertNotNull("android.request.availableCapabilities must never be null",
2159                     capabilities);
2160             boolean supports10BitOutput = arrayContains(capabilities,
2161                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT);
2162             if (!supports10BitOutput) {
2163                 continue;
2164             } else {
2165                 device10bitSupport = true;
2166             }
2167 
2168             if (CameraTestUtils.isPrimaryRearFacingCamera(mCameraManager, allCameraIds[i])) {
2169                 rearFacing10bitSupport = true;
2170             } else if (CameraTestUtils.isPrimaryFrontFacingCamera(mCameraManager,
2171                     allCameraIds[i])) {
2172                 frontFacing10bitSupport = true;
2173             }
2174         }
2175 
2176         if (device10bitSupport) {
2177             assertTrue("10-bit output support must be enabled on either front or rear " +
2178                     " camera", rearFacing10bitSupport || frontFacing10bitSupport);
2179         }
2180     }
2181 
2182     /**
2183      * The same HDR profiles must be supported for all BACKWARD_COMPATIBLE-capable physical
2184      * sub-cameras of a logical camera, and the logical camera itself.
2185      */
2186     @CddTest(requirement="7.5/C-2-3")
2187     @Test
test10BitLogicalDeviceSupport()2188     public void test10BitLogicalDeviceSupport() throws Exception {
2189         String[] allCameraIds = getAllCameraIds();
2190         for (int i = 0; i < allCameraIds.length; i++) {
2191             Log.i(TAG, "test10BitLogicalDeviceSupport: Testing camera ID " + allCameraIds[i]);
2192 
2193             CameraCharacteristics c = mCharacteristics.get(i);
2194             StaticMetadata staticMetadata = mAllStaticInfo.get(allCameraIds[i]);
2195             boolean supports10BitOutput = staticMetadata.isCapabilitySupported(
2196                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT);
2197             if (!supports10BitOutput) {
2198                 continue;
2199             }
2200 
2201             if (!staticMetadata.isColorOutputSupported()) {
2202                 continue;
2203             }
2204 
2205             if (staticMetadata.isLogicalMultiCamera()) {
2206                 Set<Long> logicalProfiles =
2207                         staticMetadata.getAvailableDynamicRangeProfilesChecked();
2208                 Set<String> physicalCameraIds = c.getPhysicalCameraIds();
2209                 for (String physicalId : physicalCameraIds) {
2210                     StaticMetadata physicalMeta = mAllStaticInfo.get(physicalId);
2211                     if (physicalMeta.isColorOutputSupported()) {
2212                         boolean physical10bitOutput =
2213                                 physicalMeta.isCapabilitySupported(CameraCharacteristics.
2214                                         REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT);
2215                         assertTrue("The logical camera: " + allCameraIds[i] +
2216                                 " 10-bit support must match with all publicly accessible color " +
2217                                 "capable physical devices: " + physicalId + " !",
2218                                 physical10bitOutput);
2219 
2220                         Set<Long> physicalProfiles =
2221                                 physicalMeta.getAvailableDynamicRangeProfilesChecked();
2222                         assertTrue("The logical camera: " + allCameraIds[i] +
2223                                 " dynamic range profiles must match with all publicly accessible " +
2224                                 "and color capable physical devices: " + physicalId + " !",
2225                                 physicalProfiles.equals(logicalProfiles));
2226                     }
2227                 }
2228             }
2229         }
2230     }
2231 
2232     @Test
2233     @ApiTest(apis = {"android.hardware.camera2.params.ColorSpaceProfiles#getProfileMap"})
testColorSpaceProfileMap()2234     public void testColorSpaceProfileMap() throws Exception {
2235         String[] allCameraIds = getAllCameraIds();
2236         for (int i = 0; i < allCameraIds.length; i++) {
2237             Log.i(TAG, "testColorSpaceProfileMap Testing camera ID " + allCameraIds[i]);
2238 
2239             CameraCharacteristics c = mCharacteristics.get(i);
2240             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
2241             assertNotNull("android.request.availableCapabilities must never be null",
2242                     capabilities);
2243             boolean supportsColorSpaceProfiles = arrayContains(capabilities,
2244                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_COLOR_SPACE_PROFILES);
2245             if (!supportsColorSpaceProfiles) {
2246                 continue;
2247             }
2248 
2249             ColorSpaceProfiles colorSpaceProfiles = c.get(
2250                     CameraCharacteristics.REQUEST_AVAILABLE_COLOR_SPACE_PROFILES);
2251             mCollector.expectNotNull("Color space profiles must always be present if the "
2252                     + "capability is reported!", colorSpaceProfiles);
2253 
2254             Map<ColorSpace.Named, Map<Integer, Set<Long>>> profileMap =
2255                     colorSpaceProfiles.getProfileMap();
2256 
2257             Set<ColorSpace.Named> colorSpaces = profileMap.keySet();
2258             mCollector.expectNotNull("profileMap.keySet() is null!", colorSpaces);
2259             mCollector.expectTrue("profileMap.keySet() is empty!", !colorSpaces.isEmpty());
2260             for (ColorSpace.Named colorSpace : colorSpaces) {
2261                 Set<Integer> imageFormats = profileMap.get(colorSpace).keySet();
2262                 mCollector.expectNotNull("profileMap.get(" + colorSpace + ").keySet() is null!",
2263                         imageFormats);
2264                 mCollector.expectTrue("profileMap.get(" + colorSpace + ").keySet() is empty!",
2265                         !imageFormats.isEmpty());
2266                 for (int imageFormat : imageFormats) {
2267                     Set<Long> dynamicRangeProfiles = profileMap.get(colorSpace).get(imageFormat);
2268                     mCollector.expectNotNull("profileMap.get(" + colorSpace + ").get("
2269                             + imageFormat + ") is null!", dynamicRangeProfiles);
2270                 }
2271             }
2272         }
2273     }
2274 
2275     @Test
2276     @ApiTest(apis = {
2277             "android.hardware.camera2.params.ColorSpaceProfiles#getSupportedColorSpaces",
2278             "android.hardware.camera2.params.ColorSpaceProfiles#getSupportedColorSpacesForDynamicRange",
2279             "android.hardware.camera2.params.ColorSpaceProfiles#getSupportedImageFormatsForColorSpace",
2280             "android.hardware.camera2.params.ColorSpaceProfiles#getSupportedDynamicRangeProfiles"})
test8BitColorSpaceOutputCharacteristics()2281     public void test8BitColorSpaceOutputCharacteristics() throws Exception {
2282         final Set<ColorSpace.Named> sdrColorSpaces = new ArraySet<>();
2283         sdrColorSpaces.add(ColorSpace.Named.SRGB);
2284         sdrColorSpaces.add(ColorSpace.Named.DISPLAY_P3);
2285 
2286         String[] allCameraIds = getAllCameraIds();
2287         for (int i = 0; i < allCameraIds.length; i++) {
2288             Log.i(TAG, "test8BitColorSpaceOutputCharacteristics: Testing camera ID "
2289                     + allCameraIds[i]);
2290 
2291             CameraCharacteristics c = mCharacteristics.get(i);
2292             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
2293             assertNotNull("android.request.availableCapabilities must never be null",
2294                     capabilities);
2295             boolean supportsColorSpaceProfiles = arrayContains(capabilities,
2296                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_COLOR_SPACE_PROFILES);
2297             if (!supportsColorSpaceProfiles) {
2298                 continue;
2299             }
2300 
2301             ColorSpaceProfiles colorSpaceProfiles = c.get(
2302                     CameraCharacteristics.REQUEST_AVAILABLE_COLOR_SPACE_PROFILES);
2303             mCollector.expectNotNull("Color space profiles must always be present if the "
2304                     + "capability is reported!", colorSpaceProfiles);
2305 
2306             Set<ColorSpace.Named> supportedColorSpacesStandard =
2307                     colorSpaceProfiles.getSupportedColorSpacesForDynamicRange(
2308                             ImageFormat.UNKNOWN, DynamicRangeProfiles.STANDARD);
2309             mCollector.expectTrue("8-bit color spaces not present!",
2310                     !supportedColorSpacesStandard.isEmpty());
2311 
2312             for (ColorSpace.Named colorSpace : supportedColorSpacesStandard) {
2313                 mCollector.expectTrue("ColorSpace " + colorSpace.ordinal() + " is not in the set"
2314                         + " of supported color spaces", sdrColorSpaces.contains(colorSpace));
2315             }
2316 
2317             Set<ColorSpace.Named> supportedColorSpaces = colorSpaceProfiles.getSupportedColorSpaces(
2318                     ImageFormat.UNKNOWN);
2319             for (ColorSpace.Named colorSpace : supportedColorSpacesStandard) {
2320                 mCollector.expectTrue("Standard color space " + colorSpace + " not present in "
2321                         + "getSupportedColorSpaces!", supportedColorSpaces.contains(colorSpace));
2322             }
2323 
2324             for (ColorSpace.Named colorSpace : supportedColorSpaces) {
2325                 Set<Integer> imageFormats =
2326                         colorSpaceProfiles.getSupportedImageFormatsForColorSpace(colorSpace);
2327                 mCollector.expectTrue("getSupportedImageFormatsForColorSpace returns an empty set "
2328                         + "for a supported color space!", !imageFormats.isEmpty());
2329 
2330                 for (int imageFormat : imageFormats) {
2331                     Set<Long> dynamicRangeProfiles =
2332                             colorSpaceProfiles.getSupportedDynamicRangeProfiles(
2333                                     colorSpace, imageFormat);
2334                     mCollector.expectTrue("getSupportedDynamicRangeProfiles returns an empty set "
2335                             + "for a supported color space and image format!",
2336                             !dynamicRangeProfiles.isEmpty());
2337                 }
2338             }
2339 
2340             for (ColorSpace.Named colorSpace : supportedColorSpacesStandard) {
2341                 Set<Integer> imageFormats =
2342                         colorSpaceProfiles.getSupportedImageFormatsForColorSpace(colorSpace);
2343                 mCollector.expectTrue("getSupportedImageFormatsForColorSpace returns an empty set "
2344                         + "for a supported color space!", !imageFormats.isEmpty());
2345 
2346                 for (int imageFormat : imageFormats) {
2347                     Set<Long> dynamicRangeProfiles =
2348                             colorSpaceProfiles.getSupportedDynamicRangeProfiles(
2349                                     colorSpace, imageFormat);
2350                     mCollector.expectTrue("getSupportedDynamicRangeProfiles returns an empty set "
2351                             + "for a supported color space and image format!",
2352                             !dynamicRangeProfiles.isEmpty());
2353                     mCollector.expectTrue("getSupportedDynamicRangeProfiles missing STANDARD for "
2354                             + "color space " + colorSpace + " and image format " + imageFormat,
2355                             dynamicRangeProfiles.contains(DynamicRangeProfiles.STANDARD));
2356                 }
2357             }
2358         }
2359     }
2360 
2361     @Test
2362     @ApiTest(apis = {
2363             "android.hardware.camera2.params.ColorSpaceProfiles#getSupportedColorSpaces",
2364             "android.hardware.camera2.params.ColorSpaceProfiles#getSupportedColorSpacesForDynamicRange",
2365             "android.hardware.camera2.params.ColorSpaceProfiles#getSupportedImageFormatsForColorSpace",
2366             "android.hardware.camera2.params.ColorSpaceProfiles#getSupportedDynamicRangeProfiles"})
test10BitColorSpaceOutputCharacteristics()2367     public void test10BitColorSpaceOutputCharacteristics() throws Exception {
2368         final Set<ColorSpace.Named> sdrColorSpaces = new ArraySet<>();
2369         sdrColorSpaces.add(ColorSpace.Named.SRGB);
2370         sdrColorSpaces.add(ColorSpace.Named.DISPLAY_P3);
2371 
2372         final Set<ColorSpace.Named> hdrColorSpaces = new ArraySet<>();
2373         hdrColorSpaces.add(ColorSpace.Named.SRGB);
2374         hdrColorSpaces.add(ColorSpace.Named.DISPLAY_P3);
2375         hdrColorSpaces.add(ColorSpace.Named.BT2020_HLG);
2376 
2377         String[] allCameraIds = getAllCameraIds();
2378         for (int i = 0; i < allCameraIds.length; i++) {
2379             Log.i(TAG, "test10BitColorSpaceOutputCharacteristics: Testing camera ID "
2380                     + allCameraIds[i]);
2381 
2382             CameraCharacteristics c = mCharacteristics.get(i);
2383             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
2384             assertNotNull("android.request.availableCapabilities must never be null",
2385                     capabilities);
2386             boolean supportsColorSpaceProfiles = arrayContains(capabilities,
2387                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_COLOR_SPACE_PROFILES);
2388             if (!supportsColorSpaceProfiles) {
2389                 Log.i(TAG, "Camera " + allCameraIds[i]
2390                         + " does not support color space profiles.");
2391                 continue;
2392             }
2393 
2394             ColorSpaceProfiles colorSpaceProfiles = c.get(
2395                     CameraCharacteristics.REQUEST_AVAILABLE_COLOR_SPACE_PROFILES);
2396             mCollector.expectNotNull("Color space profiles must always be present if the "
2397                     + "capability is reported!", colorSpaceProfiles);
2398 
2399             boolean supports10BitOutput = arrayContains(capabilities,
2400                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT);
2401             Set<ColorSpace.Named> supportedColorSpaces = null;
2402             int[] imageFormats = { ImageFormat.YCBCR_P010, ImageFormat.UNKNOWN };
2403             if (!supports10BitOutput) {
2404                 Log.i(TAG, "Camera " + allCameraIds[i]
2405                         + " does not support dynamic range profiles.");
2406                 for (int imageFormat : imageFormats) {
2407                     supportedColorSpaces = colorSpaceProfiles.getSupportedColorSpaces(imageFormat);
2408                     for (ColorSpace.Named colorSpace : supportedColorSpaces) {
2409                         Set<Long> compatibleDynamicRangeProfiles =
2410                                 colorSpaceProfiles.getSupportedDynamicRangeProfiles(colorSpace,
2411                                         imageFormat);
2412                         if (!compatibleDynamicRangeProfiles.isEmpty()) {
2413                             Long[] arrDynamicRangeProfiles =
2414                                     compatibleDynamicRangeProfiles.toArray(new Long[0]);
2415                             mCollector.expectTrue("getSupportedDynamicRangeProfiles should return a"
2416                                     + " set containing only STANDARD!",
2417                                     arrDynamicRangeProfiles.length == 1);
2418                             mCollector.expectTrue("getSupportedDynamicRangeProfiles should return a"
2419                                     + " set containing only STANDARD!",
2420                                     arrDynamicRangeProfiles[0] == DynamicRangeProfiles.STANDARD);
2421                         }
2422 
2423                         for (Long dynamicRangeProfile = DynamicRangeProfiles.STANDARD;
2424                                 dynamicRangeProfile < DynamicRangeProfiles.PUBLIC_MAX;
2425                                 dynamicRangeProfile <<= 1) {
2426                             Set<ColorSpace.Named> compatibleColorSpaces =
2427                                     colorSpaceProfiles.getSupportedColorSpacesForDynamicRange(
2428                                             imageFormat, dynamicRangeProfile);
2429                             if (dynamicRangeProfile == DynamicRangeProfiles.STANDARD) {
2430                                 mCollector.expectTrue("getSupportedColorSpacesForDynamicRange "
2431                                         + "should return a set containing STANDARD for a supported"
2432                                         + "color space and image format!",
2433                                         compatibleColorSpaces.contains(colorSpace));
2434                                 mCollector.expectTrue("ColorSpace " + colorSpace.ordinal() + " is "
2435                                         + "not in the set of supported color spaces for STANDARD",
2436                                         sdrColorSpaces.contains(colorSpace));
2437                             } else {
2438                                 mCollector.expectTrue("getSupportedColorSpacesForDynamicRange "
2439                                         + "should return an empty set for HDR!",
2440                                         compatibleColorSpaces.isEmpty());
2441                                 mCollector.expectTrue("ColorSpace " + colorSpace.ordinal() + " is "
2442                                         + "not in the set of supported color spaces for HDR",
2443                                         hdrColorSpaces.contains(colorSpace));
2444                             }
2445                         }
2446                     }
2447                 }
2448             } else {
2449                 for (int imageFormat : imageFormats) {
2450                     supportedColorSpaces = colorSpaceProfiles.getSupportedColorSpaces(
2451                             imageFormat);
2452                     DynamicRangeProfiles dynamicRangeProfiles = c.get(
2453                             CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES);
2454                     mCollector.expectNotNull("Dynamic range profile must always be present in case "
2455                             + "of 10-bit capable devices!", dynamicRangeProfiles);
2456                     Set<Long> supportedDynamicRangeProfiles =
2457                             dynamicRangeProfiles.getSupportedProfiles();
2458                     mCollector.expectTrue("Dynamic range profiles not present!",
2459                             !supportedDynamicRangeProfiles.isEmpty());
2460 
2461                     for (ColorSpace.Named colorSpace : supportedColorSpaces) {
2462                         Set<Long> compatibleDynamicRangeProfiles =
2463                                 colorSpaceProfiles.getSupportedDynamicRangeProfiles(colorSpace,
2464                                         imageFormat);
2465 
2466                         for (Long dynamicRangeProfile : compatibleDynamicRangeProfiles) {
2467                             mCollector.expectTrue("Compatible dynamic range profile not reported in"
2468                                     + " DynamicRangeProfiles!",
2469                                     supportedDynamicRangeProfiles.contains(dynamicRangeProfile));
2470 
2471                             if (dynamicRangeProfile == DynamicRangeProfiles.STANDARD) {
2472                                 mCollector.expectTrue("ColorSpace " + colorSpace.ordinal() + " is "
2473                                         + "not in the set of supported color spaces for STANDARD",
2474                                         sdrColorSpaces.contains(colorSpace));
2475                             } else {
2476                                 mCollector.expectTrue("ColorSpace " + colorSpace.ordinal() + " is "
2477                                         + "not in the set of supported color spaces for HDR",
2478                                         hdrColorSpaces.contains(colorSpace));
2479                             }
2480                         }
2481                     }
2482                 }
2483             }
2484         }
2485     }
2486 
verifyLensCalibration(float[] poseRotation, float[] poseTranslation, Integer poseReference, float[] cameraIntrinsics, float[] distortion, Rect precorrectionArray, Integer facing)2487     private void verifyLensCalibration(float[] poseRotation, float[] poseTranslation,
2488             Integer poseReference, float[] cameraIntrinsics, float[] distortion,
2489             Rect precorrectionArray, Integer facing) {
2490 
2491         mCollector.expectTrue(
2492             "LENS_POSE_ROTATION not right size",
2493             poseRotation != null && poseRotation.length == 4);
2494         mCollector.expectTrue(
2495             "LENS_POSE_TRANSLATION not right size",
2496             poseTranslation != null && poseTranslation.length == 3);
2497         mCollector.expectTrue(
2498             "LENS_POSE_REFERENCE is not defined",
2499             poseReference != null);
2500         mCollector.expectTrue(
2501             "LENS_INTRINSIC_CALIBRATION not right size",
2502             cameraIntrinsics != null && cameraIntrinsics.length == 5);
2503         mCollector.expectTrue(
2504             "LENS_DISTORTION not right size",
2505             distortion != null && distortion.length == 6);
2506 
2507         if (poseRotation != null && poseRotation.length == 4) {
2508             float normSq =
2509                     poseRotation[0] * poseRotation[0] +
2510                     poseRotation[1] * poseRotation[1] +
2511                     poseRotation[2] * poseRotation[2] +
2512                     poseRotation[3] * poseRotation[3];
2513             mCollector.expectTrue(
2514                 "LENS_POSE_ROTATION quarternion must be unit-length",
2515                 0.9999f < normSq && normSq < 1.0001f);
2516 
2517             if (facing.intValue() == CameraMetadata.LENS_FACING_FRONT ||
2518                     facing.intValue() == CameraMetadata.LENS_FACING_BACK) {
2519                 // Use the screen's natural facing to test pose rotation
2520                 int[] facingSensor = new int[]{0, 0, 1};
2521                 float[][] r = new float[][] {
2522                         { 1.0f - 2 * poseRotation[1] * poseRotation[1]
2523                               - 2 * poseRotation[2] * poseRotation[2],
2524                           2 * poseRotation[0] * poseRotation[1]
2525                               - 2 * poseRotation[2] * poseRotation[3],
2526                           2 * poseRotation[0] * poseRotation[2]
2527                               + 2 * poseRotation[1] * poseRotation[3] },
2528                         { 2 * poseRotation[0] * poseRotation[1]
2529                               + 2 * poseRotation[2] * poseRotation[3],
2530                           1.0f - 2 * poseRotation[0] * poseRotation[0]
2531                               - 2 * poseRotation[2] * poseRotation[2],
2532                           2 * poseRotation[1] * poseRotation[2]
2533                               - 2 * poseRotation[0] * poseRotation[3] },
2534                         { 2 * poseRotation[0] * poseRotation[2]
2535                               - 2 * poseRotation[1] * poseRotation[3],
2536                           2 * poseRotation[1] * poseRotation[2]
2537                               + 2 * poseRotation[0] * poseRotation[3],
2538                           1.0f - 2 * poseRotation[0] * poseRotation[0]
2539                               - 2 * poseRotation[1] * poseRotation[1] }
2540                       };
2541                 // The screen natural facing in camera's coordinate system
2542                 float facingCameraX = r[0][0] * facingSensor[0] + r[0][1] * facingSensor[1] +
2543                         r[0][2] * facingSensor[2];
2544                 float facingCameraY = r[1][0] * facingSensor[0] + r[1][1] * facingSensor[1] +
2545                         r[1][2] * facingSensor[2];
2546                 float facingCameraZ = r[2][0] * facingSensor[0] + r[2][1] * facingSensor[1] +
2547                         r[2][2] * facingSensor[2];
2548 
2549                 mCollector.expectTrue("LENS_POSE_ROTATION must be consistent with lens facing",
2550                         (facingCameraZ > 0) ^
2551                         (facing.intValue() == CameraMetadata.LENS_FACING_BACK));
2552 
2553                 if (poseReference == CameraCharacteristics.LENS_POSE_REFERENCE_UNDEFINED) {
2554                     mCollector.expectTrue(
2555                             "LENS_POSE_ROTATION quarternion must be consistent with camera's " +
2556                             "default facing",
2557                             Math.abs(facingCameraX) < 0.00001f &&
2558                             Math.abs(facingCameraY) < 0.00001f &&
2559                             Math.abs(facingCameraZ) > 0.99999f &&
2560                             Math.abs(facingCameraZ) < 1.00001f);
2561                 }
2562             }
2563 
2564             // TODO: Cross-validate orientation and poseRotation
2565         }
2566 
2567         if (poseTranslation != null && poseTranslation.length == 3) {
2568             float normSq =
2569                     poseTranslation[0] * poseTranslation[0] +
2570                     poseTranslation[1] * poseTranslation[1] +
2571                     poseTranslation[2] * poseTranslation[2];
2572             mCollector.expectTrue("Pose translation is larger than 1 m",
2573                     normSq < 1.f);
2574 
2575             // Pose translation should be all 0s for UNDEFINED pose reference.
2576             if (poseReference != null && poseReference ==
2577                     CameraCharacteristics.LENS_POSE_REFERENCE_UNDEFINED) {
2578                 mCollector.expectTrue("Pose translation aren't all 0s ",
2579                         normSq < 0.00001f);
2580             }
2581         }
2582 
2583         if (poseReference != null) {
2584             int ref = poseReference;
2585             boolean validReference = false;
2586             switch (ref) {
2587                 case CameraCharacteristics.LENS_POSE_REFERENCE_PRIMARY_CAMERA:
2588                 case CameraCharacteristics.LENS_POSE_REFERENCE_GYROSCOPE:
2589                 case CameraCharacteristics.LENS_POSE_REFERENCE_UNDEFINED:
2590                     // Allowed values
2591                     validReference = true;
2592                     break;
2593                 default:
2594             }
2595             mCollector.expectTrue("POSE_REFERENCE has unknown value", validReference);
2596         }
2597 
2598         mCollector.expectTrue("Does not have precorrection active array defined",
2599                 precorrectionArray != null);
2600 
2601         if (cameraIntrinsics != null && precorrectionArray != null) {
2602             float fx = cameraIntrinsics[0];
2603             float fy = cameraIntrinsics[1];
2604             float cx = cameraIntrinsics[2];
2605             float cy = cameraIntrinsics[3];
2606             float s = cameraIntrinsics[4];
2607             mCollector.expectTrue("Optical center expected to be within precorrection array",
2608                     0 <= cx && cx < precorrectionArray.width() &&
2609                     0 <= cy && cy < precorrectionArray.height());
2610 
2611             // TODO: Verify focal lengths and skew are reasonable
2612         }
2613 
2614         if (distortion != null) {
2615             // TODO: Verify radial distortion
2616         }
2617 
2618     }
2619 
2620     /**
2621      * Cross-check StreamConfigurationMap output
2622      */
2623     @Test
testStreamConfigurationMap()2624     public void testStreamConfigurationMap() throws Exception {
2625         String[] allCameraIds = getAllCameraIds();
2626         for (int i = 0; i < allCameraIds.length; i++) {
2627             Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + allCameraIds[i]);
2628             CameraCharacteristics c = mCharacteristics.get(i);
2629             StreamConfigurationMap config =
2630                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
2631             assertNotNull(String.format("No stream configuration map found for: ID %s",
2632                             allCameraIds[i]), config);
2633 
2634             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
2635             assertNotNull("android.request.availableCapabilities must never be null",
2636                     actualCapabilities);
2637 
2638             if (arrayContains(actualCapabilities, BC)) {
2639                 assertTrue("ImageReader must be supported",
2640                     config.isOutputSupportedFor(android.media.ImageReader.class));
2641                 assertTrue("MediaRecorder must be supported",
2642                     config.isOutputSupportedFor(android.media.MediaRecorder.class));
2643                 assertTrue("MediaCodec must be supported",
2644                     config.isOutputSupportedFor(android.media.MediaCodec.class));
2645                 assertTrue("SurfaceHolder must be supported",
2646                     config.isOutputSupportedFor(android.view.SurfaceHolder.class));
2647                 assertTrue("SurfaceTexture must be supported",
2648                     config.isOutputSupportedFor(android.graphics.SurfaceTexture.class));
2649 
2650                 assertTrue("YUV_420_888 must be supported",
2651                     config.isOutputSupportedFor(ImageFormat.YUV_420_888));
2652                 assertTrue("JPEG must be supported",
2653                     config.isOutputSupportedFor(ImageFormat.JPEG));
2654             } else {
2655                 assertTrue("YUV_420_88 may not be supported if BACKWARD_COMPATIBLE capability is not listed",
2656                     !config.isOutputSupportedFor(ImageFormat.YUV_420_888));
2657                 assertTrue("JPEG may not be supported if BACKWARD_COMPATIBLE capability is not listed",
2658                     !config.isOutputSupportedFor(ImageFormat.JPEG));
2659             }
2660 
2661             // Check RAW
2662 
2663             if (arrayContains(actualCapabilities,
2664                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
2665                 assertTrue("RAW_SENSOR must be supported if RAW capability is advertised",
2666                     config.isOutputSupportedFor(ImageFormat.RAW_SENSOR));
2667             }
2668 
2669             // Cross check public formats and sizes
2670 
2671             int[] supportedFormats = config.getOutputFormats();
2672             for (int format : supportedFormats) {
2673                 assertTrue("Format " + format + " fails cross check",
2674                         config.isOutputSupportedFor(format));
2675                 List<Size> supportedSizes = CameraTestUtils.getAscendingOrderSizes(
2676                         Arrays.asList(config.getOutputSizes(format)), /*ascending*/true);
2677                 if (arrayContains(actualCapabilities,
2678                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
2679                     supportedSizes.addAll(
2680                         Arrays.asList(config.getHighResolutionOutputSizes(format)));
2681                     supportedSizes = CameraTestUtils.getAscendingOrderSizes(
2682                         supportedSizes, /*ascending*/true);
2683                 }
2684                 assertTrue("Supported format " + format + " has no sizes listed",
2685                         supportedSizes.size() > 0);
2686                 for (int j = 0; j < supportedSizes.size(); j++) {
2687                     Size size = supportedSizes.get(j);
2688                     if (VERBOSE) {
2689                         Log.v(TAG,
2690                                 String.format("Testing camera %s, format %d, size %s",
2691                                         allCameraIds[i], format, size.toString()));
2692                     }
2693 
2694                     long stallDuration = config.getOutputStallDuration(format, size);
2695                     switch(format) {
2696                         case ImageFormat.YUV_420_888:
2697                             assertTrue("YUV_420_888 may not have a non-zero stall duration",
2698                                     stallDuration == 0);
2699                             break;
2700                         case ImageFormat.JPEG:
2701                         case ImageFormat.RAW_SENSOR:
2702                             final float TOLERANCE_FACTOR = 2.0f;
2703                             long prevDuration = 0;
2704                             if (j > 0) {
2705                                 prevDuration = config.getOutputStallDuration(
2706                                         format, supportedSizes.get(j - 1));
2707                             }
2708                             long nextDuration = Long.MAX_VALUE;
2709                             if (j < (supportedSizes.size() - 1)) {
2710                                 nextDuration = config.getOutputStallDuration(
2711                                         format, supportedSizes.get(j + 1));
2712                             }
2713                             long curStallDuration = config.getOutputStallDuration(format, size);
2714                             // Stall duration should be in a reasonable range: larger size should
2715                             // normally have larger stall duration.
2716                             mCollector.expectInRange("Stall duration (format " + format +
2717                                     " and size " + size + ") is not in the right range",
2718                                     curStallDuration,
2719                                     (long) (prevDuration / TOLERANCE_FACTOR),
2720                                     (long) (nextDuration * TOLERANCE_FACTOR));
2721                             break;
2722                         default:
2723                             assertTrue("Negative stall duration for format " + format,
2724                                     stallDuration >= 0);
2725                             break;
2726                     }
2727                     long minDuration = config.getOutputMinFrameDuration(format, size);
2728                     if (arrayContains(actualCapabilities,
2729                             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
2730                         assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
2731                                 + "format " + format + " for size " + size + " minDuration " +
2732                                 minDuration,
2733                                 minDuration > 0);
2734                     } else {
2735                         assertTrue("Need non-negative min frame duration for format " + format,
2736                                 minDuration >= 0);
2737                     }
2738 
2739                     if (Flags.cameraHeifGainmap() && (format == ImageFormat.HEIC_ULTRAHDR)) {
2740                         //  Currently ImageReader cannot map HEIC_ULTRAHDR without explicit
2741                         //  hardware buffer and dataspace configuration.
2742                         // TODO: b/364911926 Remove this check once DataSpace.DATASPACE_HEIF_ULTRAHDR
2743                         //     is available end to end.
2744                         ImageReader.Builder testReaderBuilder = new ImageReader.Builder(
2745                                 size.getWidth(),
2746                                 size.getHeight());
2747 
2748                         testReaderBuilder.setImageFormat(HardwareBuffer.BLOB);
2749                         testReaderBuilder.setDefaultHardwareBufferFormat(HardwareBuffer.BLOB);
2750                         testReaderBuilder.setDefaultDataSpace(DataSpace.DATASPACE_HEIF_ULTRAHDR);
2751                         testReaderBuilder.setMaxImages(1);
2752                         ImageReader testReader = testReaderBuilder.build();
2753                         Surface testSurface = testReader.getSurface();
2754 
2755                         assertTrue(
2756                                 String.format("isOutputSupportedFor fails for config %s, format %d",
2757                                         size.toString(), format),
2758                                 config.isOutputSupportedFor(testSurface));
2759 
2760                         testReader.close();
2761                     } else if (format != ImageFormat.PRIVATE) {
2762                         // todo: test opaque image reader when it's supported.
2763                         ImageReader testReader = ImageReader.newInstance(
2764                             size.getWidth(),
2765                             size.getHeight(),
2766                             format,
2767                             1);
2768                         Surface testSurface = testReader.getSurface();
2769 
2770                         assertTrue(
2771                             String.format("isOutputSupportedFor fails for config %s, format %d",
2772                                     size.toString(), format),
2773                             config.isOutputSupportedFor(testSurface));
2774 
2775                         testReader.close();
2776                     }
2777                 } // sizes
2778 
2779                 // Try an invalid size in this format, should round
2780                 Size invalidSize = findInvalidSize(supportedSizes);
2781                 int MAX_ROUNDING_WIDTH = 1920;
2782                 if (Flags.cameraHeifGainmap() && (format == ImageFormat.HEIC_ULTRAHDR)) {
2783                     //  Currently ImageReader cannot map HEIC_ULTRAHDR without explicit
2784                     //  hardware buffer and dataspace configuration.
2785                     // TODO: b/364911926 Remove this check once DataSpace.DATASPACE_HEIF_ULTRAHDR
2786                     //     is available end to end.
2787                     ImageReader.Builder testReaderBuilder = new ImageReader.Builder(
2788                             invalidSize.getWidth(),
2789                             invalidSize.getHeight());
2790 
2791                     testReaderBuilder.setImageFormat(HardwareBuffer.BLOB);
2792                     testReaderBuilder.setDefaultHardwareBufferFormat(HardwareBuffer.BLOB);
2793                     testReaderBuilder.setDefaultDataSpace(DataSpace.DATASPACE_HEIF_ULTRAHDR);
2794                     testReaderBuilder.setMaxImages(1);
2795                     ImageReader testReader = testReaderBuilder.build();
2796                     Surface testSurface = testReader.getSurface();
2797 
2798                     assertTrue(
2799                             String.format("isOutputSupportedFor fails for config %s, format %d",
2800                                     invalidSize.toString(), format),
2801                             config.isOutputSupportedFor(testSurface));
2802 
2803                     testReader.close();
2804                 } else if (format != ImageFormat.PRIVATE &&
2805                         invalidSize.getWidth() <= MAX_ROUNDING_WIDTH) {
2806                     // todo: test opaque image reader when it's supported.
2807                     ImageReader testReader = ImageReader.newInstance(
2808                                                                      invalidSize.getWidth(),
2809                                                                      invalidSize.getHeight(),
2810                                                                      format,
2811                                                                      1);
2812                     Surface testSurface = testReader.getSurface();
2813 
2814                     assertTrue(
2815                                String.format("isOutputSupportedFor fails for config %s, %d",
2816                                        invalidSize.toString(), format),
2817                                config.isOutputSupportedFor(testSurface));
2818 
2819                     testReader.close();
2820                 }
2821             } // formats
2822 
2823             // Cross-check opaque format and sizes
2824             if (arrayContains(actualCapabilities, BC)) {
2825                 SurfaceTexture st = new SurfaceTexture(1);
2826                 Surface surf = new Surface(st);
2827 
2828                 Size[] opaqueSizes = CameraTestUtils.getSupportedSizeForClass(SurfaceTexture.class,
2829                         allCameraIds[i], mCameraManager);
2830                 assertTrue("Opaque format has no sizes listed",
2831                         opaqueSizes.length > 0);
2832                 for (Size size : opaqueSizes) {
2833                     long stallDuration = config.getOutputStallDuration(SurfaceTexture.class, size);
2834                     assertTrue("Opaque output may not have a non-zero stall duration",
2835                             stallDuration == 0);
2836 
2837                     long minDuration = config.getOutputMinFrameDuration(SurfaceTexture.class, size);
2838                     if (arrayContains(actualCapabilities,
2839                                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
2840                         assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
2841                                 + "opaque format",
2842                                 minDuration > 0);
2843                     } else {
2844                         assertTrue("Need non-negative min frame duration for opaque format ",
2845                                 minDuration >= 0);
2846                     }
2847                     st.setDefaultBufferSize(size.getWidth(), size.getHeight());
2848 
2849                     assertTrue(
2850                             String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
2851                                     size.toString()),
2852                             config.isOutputSupportedFor(surf));
2853 
2854                 } // opaque sizes
2855 
2856                 // Try invalid opaque size, should get rounded
2857                 Size invalidSize = findInvalidSize(opaqueSizes);
2858                 st.setDefaultBufferSize(invalidSize.getWidth(), invalidSize.getHeight());
2859                 assertTrue(
2860                         String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
2861                                 invalidSize.toString()),
2862                         config.isOutputSupportedFor(surf));
2863 
2864             }
2865         } // mCharacteristics
2866     }
2867 
2868     /**
2869      * Test high speed capability and cross-check the high speed sizes and fps ranges from
2870      * the StreamConfigurationMap.
2871      */
2872     @Test
testConstrainedHighSpeedCapability()2873     public void testConstrainedHighSpeedCapability() throws Exception {
2874         String[] allCameraIds = getAllCameraIds();
2875         for (int i = 0; i < allCameraIds.length; i++) {
2876             CameraCharacteristics c = mCharacteristics.get(i);
2877             int[] capabilities = CameraTestUtils.getValueNotNull(
2878                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
2879             boolean supportHighSpeed = arrayContains(capabilities, CONSTRAINED_HIGH_SPEED);
2880             if (supportHighSpeed) {
2881                 StreamConfigurationMap config =
2882                         CameraTestUtils.getValueNotNull(
2883                                 c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
2884                 List<Size> highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes());
2885                 assertTrue("High speed sizes shouldn't be empty", highSpeedSizes.size() > 0);
2886                 Size[] allSizes = CameraTestUtils.getSupportedSizeForFormat(ImageFormat.PRIVATE,
2887                         allCameraIds[i], mCameraManager);
2888                 assertTrue("Normal size for PRIVATE format shouldn't be null or empty",
2889                         allSizes != null && allSizes.length > 0);
2890                 for (Size size: highSpeedSizes) {
2891                     // The sizes must be a subset of the normal sizes
2892                     assertTrue("High speed size " + size +
2893                             " must be part of normal sizes " + Arrays.toString(allSizes),
2894                             Arrays.asList(allSizes).contains(size));
2895 
2896                     // Sanitize the high speed FPS ranges for each size
2897                     List<Range<Integer>> ranges =
2898                             Arrays.asList(config.getHighSpeedVideoFpsRangesFor(size));
2899                     int previewFps = Integer.MAX_VALUE;
2900                     for (Range<Integer> range : ranges) {
2901                         int rangeMin = range.getLower();
2902                         if (previewFps > rangeMin) {
2903                             previewFps = rangeMin;
2904                         }
2905                     }
2906                     Log.v(TAG, "Advertised preview fps is: " + previewFps);
2907                     // We only support preview of 30fps or 60fps.
2908                     assertTrue("Preview fps " + previewFps + " is not valid.",
2909                             (previewFps == 30 || previewFps == 60));
2910                     for (Range<Integer> range : ranges) {
2911                         assertTrue("The range " + range + " doesn't satisfy the"
2912                                 + " min/max boundary requirements.",
2913                                 range.getLower() >= HIGH_SPEED_FPS_LOWER_MIN &&
2914                                 range.getUpper() >= HIGH_SPEED_FPS_UPPER_MIN);
2915                         assertTrue("The range " + range + " should be multiple of 30fps",
2916                                 range.getLower() % 30 == 0 && range.getUpper() % 30 == 0);
2917                         // If the range is fixed high speed range, it should contain the
2918                         // [previewFps, fps_max] in the high speed range list; if it's variable FPS
2919                         // range, the corresponding fixed FPS Range must be included in the range
2920                         // list.
2921                         if (Objects.equals(range.getLower(), range.getUpper())) {
2922                             Range<Integer> variableRange = new Range<Integer>(previewFps,
2923                                     range.getUpper());
2924                             assertTrue("The variable FPS range " + variableRange +
2925                                     " shoould be included in the high speed ranges for size " +
2926                                     size, ranges.contains(variableRange));
2927                         } else {
2928                             Range<Integer> fixedRange =
2929                                     new Range<Integer>(range.getUpper(), range.getUpper());
2930                             assertTrue("The fixed FPS range " + fixedRange +
2931                                     " shoould be included in the high speed ranges for size " +
2932                                     size, ranges.contains(fixedRange));
2933                         }
2934                     }
2935                 }
2936                 // If the device advertise some high speed profiles, the sizes and FPS ranges
2937                 // should be advertise by the camera.
2938                 for (int quality = CamcorderProfile.QUALITY_HIGH_SPEED_480P;
2939                         quality <= CamcorderProfile.QUALITY_HIGH_SPEED_2160P; quality++) {
2940                     int cameraId = Integer.valueOf(allCameraIds[i]);
2941                     if (CamcorderProfile.hasProfile(cameraId, quality)) {
2942                         CamcorderProfile profile = CamcorderProfile.get(cameraId, quality);
2943                         Size camcorderProfileSize =
2944                                 new Size(profile.videoFrameWidth, profile.videoFrameHeight);
2945                         assertTrue("CamcorderPrfile size " + camcorderProfileSize +
2946                                 " must be included in the high speed sizes " +
2947                                 Arrays.toString(highSpeedSizes.toArray()),
2948                                 highSpeedSizes.contains(camcorderProfileSize));
2949                         Range<Integer> camcorderFpsRange =
2950                                 new Range<Integer>(profile.videoFrameRate, profile.videoFrameRate);
2951                         List<Range<Integer>> allRanges =
2952                                 Arrays.asList(config.getHighSpeedVideoFpsRangesFor(
2953                                         camcorderProfileSize));
2954                         assertTrue("Camcorder fps range " + camcorderFpsRange +
2955                                 " should be included by high speed fps ranges " +
2956                                 Arrays.toString(allRanges.toArray()),
2957                                 allRanges.contains(camcorderFpsRange));
2958                     }
2959                 }
2960             }
2961         }
2962     }
2963 
2964     /**
2965      * Correctness check of optical black regions.
2966      */
2967     @Test
testOpticalBlackRegions()2968     public void testOpticalBlackRegions() throws Exception {
2969         String[] allCameraIds = getAllCameraIds();
2970         for (int i = 0; i < allCameraIds.length; i++) {
2971             CameraCharacteristics c = mCharacteristics.get(i);
2972             List<CaptureResult.Key<?>> resultKeys = c.getAvailableCaptureResultKeys();
2973             boolean hasDynamicBlackLevel =
2974                     resultKeys.contains(CaptureResult.SENSOR_DYNAMIC_BLACK_LEVEL);
2975             boolean hasDynamicWhiteLevel =
2976                     resultKeys.contains(CaptureResult.SENSOR_DYNAMIC_WHITE_LEVEL);
2977             boolean hasFixedBlackLevel =
2978                     c.getKeys().contains(CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN);
2979             boolean hasFixedWhiteLevel =
2980                     c.getKeys().contains(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
2981             // The black and white levels should be either all supported or none of them is
2982             // supported.
2983             mCollector.expectTrue("Dynamic black and white level should be all or none of them"
2984                     + " be supported", hasDynamicWhiteLevel == hasDynamicBlackLevel);
2985             mCollector.expectTrue("Fixed black and white level should be all or none of them"
2986                     + " be supported", hasFixedBlackLevel == hasFixedWhiteLevel);
2987             mCollector.expectTrue("Fixed black level should be supported if dynamic black"
2988                     + " level is supported", !hasDynamicBlackLevel || hasFixedBlackLevel);
2989 
2990             if (c.getKeys().contains(CameraCharacteristics.SENSOR_OPTICAL_BLACK_REGIONS)) {
2991                 // Regions shouldn't be null or empty.
2992                 Rect[] regions = CameraTestUtils.getValueNotNull(c,
2993                         CameraCharacteristics.SENSOR_OPTICAL_BLACK_REGIONS);
2994                 CameraTestUtils.assertArrayNotEmpty(regions, "Optical back region arrays must not"
2995                         + " be empty");
2996 
2997                 // Dynamic black level should be supported if the optical black region is
2998                 // advertised.
2999                 mCollector.expectTrue("Dynamic black and white level keys should be advertised in "
3000                         + "available capture result key list", hasDynamicWhiteLevel);
3001 
3002                 // Range check.
3003                 for (Rect region : regions) {
3004                     mCollector.expectTrue("Camera " + allCameraIds[i] + ": optical black region" +
3005                             " shouldn't be empty!", !region.isEmpty());
3006                     mCollector.expectGreaterOrEqual("Optical black region left", 0/*expected*/,
3007                             region.left/*actual*/);
3008                     mCollector.expectGreaterOrEqual("Optical black region top", 0/*expected*/,
3009                             region.top/*actual*/);
3010                     mCollector.expectTrue("Optical black region left/right/width/height must be"
3011                             + " even number, otherwise, the bayer CFA pattern in this region will"
3012                             + " be messed up",
3013                             region.left % 2 == 0 && region.top % 2 == 0 &&
3014                             region.width() % 2 == 0 && region.height() % 2 == 0);
3015                     mCollector.expectGreaterOrEqual("Optical black region top", 0/*expected*/,
3016                             region.top/*actual*/);
3017                     Size size = CameraTestUtils.getValueNotNull(c,
3018                             CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
3019                     mCollector.expectLessOrEqual("Optical black region width",
3020                             size.getWidth()/*expected*/, region.width());
3021                     mCollector.expectLessOrEqual("Optical black region height",
3022                             size.getHeight()/*expected*/, region.height());
3023                     Rect activeArray = CameraTestUtils.getValueNotNull(c,
3024                             CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
3025                     mCollector.expectTrue("Optical black region" + region + " should be outside of"
3026                             + " active array " + activeArray,
3027                             !region.intersect(activeArray));
3028                     // Region need to be disjoint:
3029                     for (Rect region2 : regions) {
3030                         mCollector.expectTrue("Optical black region" + region + " should have no "
3031                                 + "overlap with " + region2,
3032                                 region == region2 || !region.intersect(region2));
3033                     }
3034                 }
3035             } else {
3036                 Log.i(TAG, "Camera " + allCameraIds[i] + " doesn't support optical black regions,"
3037                         + " skip the region test");
3038             }
3039         }
3040     }
3041 
3042     /**
3043      * Check Logical camera capability
3044      */
3045     @Test
testLogicalCameraCharacteristics()3046     public void testLogicalCameraCharacteristics() throws Exception {
3047         String[] allCameraIds = getAllCameraIds();
3048         for (int i = 0; i < allCameraIds.length; i++) {
3049             CameraCharacteristics c = mCharacteristics.get(i);
3050             int[] capabilities = CameraTestUtils.getValueNotNull(
3051                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
3052             boolean supportLogicalCamera = arrayContains(capabilities,
3053                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA);
3054             if (supportLogicalCamera) {
3055                 Set<String> physicalCameraIds = c.getPhysicalCameraIds();
3056                 assertNotNull("android.logicalCam.physicalCameraIds shouldn't be null",
3057                     physicalCameraIds);
3058                 assertTrue("Logical camera must contain at least 2 physical camera ids",
3059                     physicalCameraIds.size() >= 2);
3060 
3061                 mCollector.expectKeyValueInRange(c,
3062                         CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE,
3063                         CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE,
3064                         CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED);
3065 
3066                 Integer timestampSource = c.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
3067                 for (String physicalCameraId : physicalCameraIds) {
3068                     assertNotNull("Physical camera id shouldn't be null", physicalCameraId);
3069                     assertTrue(
3070                             String.format("Physical camera id %s shouldn't be the same as logical"
3071                                     + " camera id %s", physicalCameraId, allCameraIds[i]),
3072                             physicalCameraId != allCameraIds[i]);
3073 
3074                     //validation for depth static metadata of physical cameras
3075                     CameraCharacteristics pc =
3076                             mCameraManager.getCameraCharacteristics(physicalCameraId);
3077 
3078                     float[] poseRotation = pc.get(CameraCharacteristics.LENS_POSE_ROTATION);
3079                     float[] poseTranslation = pc.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
3080                     Integer poseReference = pc.get(CameraCharacteristics.LENS_POSE_REFERENCE);
3081                     float[] cameraIntrinsics = pc.get(
3082                             CameraCharacteristics.LENS_INTRINSIC_CALIBRATION);
3083                     float[] distortion = getLensDistortion(pc);
3084                     Integer facing = pc.get(CameraCharacteristics.LENS_FACING);
3085                     Rect precorrectionArray = pc.get(
3086                             CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
3087 
3088                     verifyLensCalibration(poseRotation, poseTranslation, poseReference,
3089                             cameraIntrinsics, distortion, precorrectionArray, facing);
3090 
3091                     Integer timestampSourcePhysical =
3092                             pc.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
3093                     mCollector.expectEquals("Logical camera and physical cameras must have same " +
3094                             "timestamp source", timestampSource, timestampSourcePhysical);
3095                 }
3096             }
3097 
3098             // Verify that if multiple focal lengths or apertures are supported, they are in
3099             // ascending order.
3100             Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
3101             boolean isExternalCamera = (hwLevel ==
3102                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
3103             if (!isExternalCamera) {
3104                 float[] focalLengths = c.get(
3105                         CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
3106                 for (int j = 0; j < focalLengths.length-1; j++) {
3107                     mCollector.expectTrue("Camera's available focal lengths must be ascending!",
3108                             focalLengths[j] < focalLengths[j+1]);
3109                 }
3110                 float[] apertures = c.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES);
3111                 for (int j = 0; j < apertures.length-1; j++) {
3112                     mCollector.expectTrue("Camera's available apertures must be ascending!",
3113                             apertures[j] < apertures[j+1]);
3114                 }
3115             }
3116         }
3117     }
3118 
3119     /**
3120      * Check manual flash control capability
3121      */
3122     @Test
testManualFlashStrengthControlCharacteristics()3123     public void testManualFlashStrengthControlCharacteristics() throws Exception {
3124         String[] allCameraIds = getAllCameraIds();
3125         for (int i = 0; i < allCameraIds.length; i++) {
3126             Log.i(TAG, "testManualFlashStrengthControlCharacteristics: Testing camera ID "
3127                     + allCameraIds[i]);
3128             CameraCharacteristics c = mCharacteristics.get(i);
3129             if (!arrayContains(getCameraIdsUnderTest(), allCameraIds[i])) {
3130                 // Skip hidden physical cameras
3131                 continue;
3132             }
3133             int singleDefaultLevel =
3134                     c.get(CameraCharacteristics.FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL);
3135             assertTrue("singleDefaultLevel must be >=1", singleDefaultLevel >= 1);
3136             int singleMaxLevel = c.get(CameraCharacteristics.FLASH_SINGLE_STRENGTH_MAX_LEVEL);
3137             assertTrue("singleMaxLevel must be >=1", singleMaxLevel >= 1);
3138             int torchDefaultLevel = c.get(CameraCharacteristics.FLASH_TORCH_STRENGTH_DEFAULT_LEVEL);
3139             assertTrue("torchDefaultLevel must be >=1", torchDefaultLevel >= 1);
3140             int torchMaxLevel = c.get(CameraCharacteristics.FLASH_TORCH_STRENGTH_MAX_LEVEL);
3141             assertTrue("torchMaxLevel must be >=1", torchMaxLevel >= 1);
3142             // Verify that the default level is not greater than the max level.
3143             assertTrue("singleDefaultLevel cannot be greater than singleMaxLevel",
3144                     singleDefaultLevel <= singleMaxLevel);
3145             assertTrue("torchDefaultLevel cannot be greater than torchMaxLevel",
3146                     torchDefaultLevel <= torchMaxLevel);
3147         }
3148     }
3149 
3150     /**
3151      * Check monochrome camera capability
3152      */
3153     @Test
testMonochromeCharacteristics()3154     public void testMonochromeCharacteristics() throws Exception {
3155         String[] allCameraIds = getAllCameraIds();
3156         for (int i = 0; i < allCameraIds.length; i++) {
3157             Log.i(TAG, "testMonochromeCharacteristics: Testing camera ID " + allCameraIds[i]);
3158 
3159             CameraCharacteristics c = mCharacteristics.get(i);
3160             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
3161             assertNotNull("android.request.availableCapabilities must never be null",
3162                     capabilities);
3163             boolean supportMonochrome = arrayContains(capabilities, MONOCHROME);
3164 
3165             if (!supportMonochrome) {
3166                 continue;
3167             }
3168 
3169             List<Key<?>> allKeys = c.getKeys();
3170             List<CaptureRequest.Key<?>> requestKeys = c.getAvailableCaptureRequestKeys();
3171             List<CaptureResult.Key<?>> resultKeys = c.getAvailableCaptureResultKeys();
3172 
3173             assertTrue("Monochrome camera must have BACKWARD_COMPATIBLE capability",
3174                     arrayContains(capabilities, BC));
3175             int colorFilterArrangement = c.get(
3176                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
3177             assertTrue("Monochrome camera must have either MONO or NIR color filter pattern",
3178                     colorFilterArrangement ==
3179                             CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO
3180                     || colorFilterArrangement ==
3181                             CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR);
3182 
3183             assertFalse("Monochrome camera must not contain SENSOR_CALIBRATION_TRANSFORM1 key",
3184                     allKeys.contains(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1));
3185             assertFalse("Monochrome camera must not contain SENSOR_COLOR_TRANSFORM1 key",
3186                     allKeys.contains(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1));
3187             assertFalse("Monochrome camera must not contain SENSOR_FORWARD_MATRIX1 key",
3188                     allKeys.contains(CameraCharacteristics.SENSOR_FORWARD_MATRIX1));
3189             assertFalse("Monochrome camera must not contain SENSOR_REFERENCE_ILLUMINANT1 key",
3190                     allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1));
3191             assertFalse("Monochrome camera must not contain SENSOR_CALIBRATION_TRANSFORM2 key",
3192                     allKeys.contains(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2));
3193             assertFalse("Monochrome camera must not contain SENSOR_COLOR_TRANSFORM2 key",
3194                     allKeys.contains(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2));
3195             assertFalse("Monochrome camera must not contain SENSOR_FORWARD_MATRIX2 key",
3196                     allKeys.contains(CameraCharacteristics.SENSOR_FORWARD_MATRIX2));
3197             assertFalse("Monochrome camera must not contain SENSOR_REFERENCE_ILLUMINANT2 key",
3198                     allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2));
3199 
3200             assertFalse(
3201                     "Monochrome capture result must not contain SENSOR_NEUTRAL_COLOR_POINT key",
3202                     resultKeys.contains(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT));
3203             assertFalse("Monochrome capture result must not contain SENSOR_GREEN_SPLIT key",
3204                     resultKeys.contains(CaptureResult.SENSOR_GREEN_SPLIT));
3205 
3206             // Check that color correction tags are not available for monochrome cameras
3207             assertTrue("Monochrome camera must not have MANUAL_POST_PROCESSING capability",
3208                     !arrayContains(capabilities, MANUAL_POSTPROC));
3209             assertTrue("Monochrome camera must not have COLOR_CORRECTION_MODE in request keys",
3210                     !requestKeys.contains(CaptureRequest.COLOR_CORRECTION_MODE));
3211             assertTrue("Monochrome camera must not have COLOR_CORRECTION_MODE in result keys",
3212                     !resultKeys.contains(CaptureResult.COLOR_CORRECTION_MODE));
3213             assertTrue("Monochrome camera must not have COLOR_CORRECTION_TRANSFORM in request keys",
3214                     !requestKeys.contains(CaptureRequest.COLOR_CORRECTION_TRANSFORM));
3215             assertTrue("Monochrome camera must not have COLOR_CORRECTION_TRANSFORM in result keys",
3216                     !resultKeys.contains(CaptureResult.COLOR_CORRECTION_TRANSFORM));
3217             assertTrue("Monochrome camera must not have COLOR_CORRECTION_GAINS in request keys",
3218                     !requestKeys.contains(CaptureRequest.COLOR_CORRECTION_GAINS));
3219             assertTrue("Monochrome camera must not have COLOR_CORRECTION_GAINS in result keys",
3220                     !resultKeys.contains(CaptureResult.COLOR_CORRECTION_GAINS));
3221 
3222             // Check that awbSupportedModes only contains AUTO
3223             int[] awbAvailableModes = c.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES);
3224             assertTrue("availableAwbModes must not be null", awbAvailableModes != null);
3225             assertTrue("availableAwbModes must contain only AUTO", awbAvailableModes.length == 1 &&
3226                     awbAvailableModes[0] == CaptureRequest.CONTROL_AWB_MODE_AUTO);
3227         }
3228     }
3229 
3230     /**
3231      * Check rotate-and-crop camera reporting.
3232      * Every device must report NONE; if actually supporting feature, must report NONE, 90, AUTO at
3233      * least.
3234      */
3235     @Test
testRotateAndCropCharacteristics()3236     public void testRotateAndCropCharacteristics() throws Exception {
3237         String[] allCameraIds = getAllCameraIds();
3238         for (int i = 0; i < allCameraIds.length; i++) {
3239             Log.i(TAG, "testRotateAndCropCharacteristics: Testing camera ID " + allCameraIds[i]);
3240 
3241             CameraCharacteristics c = mCharacteristics.get(i);
3242 
3243             if (!arrayContains(getCameraIdsUnderTest(), allCameraIds[i])) {
3244                 // Skip hidden physical cameras
3245                 continue;
3246             }
3247 
3248             int[] availableRotateAndCropModes = c.get(
3249                     CameraCharacteristics.SCALER_AVAILABLE_ROTATE_AND_CROP_MODES);
3250             assertTrue("availableRotateAndCropModes must not be null",
3251                      availableRotateAndCropModes != null);
3252             boolean foundAuto = false;
3253             boolean foundNone = false;
3254             boolean found90 = false;
3255             for (int mode :  availableRotateAndCropModes) {
3256                 switch(mode) {
3257                     case CameraCharacteristics.SCALER_ROTATE_AND_CROP_NONE:
3258                         foundNone = true;
3259                         break;
3260                     case CameraCharacteristics.SCALER_ROTATE_AND_CROP_90:
3261                         found90 = true;
3262                         break;
3263                     case CameraCharacteristics.SCALER_ROTATE_AND_CROP_AUTO:
3264                         foundAuto = true;
3265                         break;
3266                 }
3267             }
3268             if (availableRotateAndCropModes.length > 1) {
3269                 assertTrue("To support SCALER_ROTATE_AND_CROP: NONE, 90, and AUTO must be included",
3270                         foundNone && found90 && foundAuto);
3271             } else {
3272                 assertTrue("If only one SCALER_ROTATE_AND_CROP value is supported, it must be NONE",
3273                         foundNone);
3274             }
3275         }
3276     }
3277 
3278     /**
3279      * Check DeviceStateSensorOrientationMap camera reporting.
3280      * If present, the map should only be part of logical camera characteristics.
3281      * Verify that all device state modes return valid orientations.
3282      */
3283     @Test
testDeviceStateSensorOrientationMapCharacteristics()3284     public void testDeviceStateSensorOrientationMapCharacteristics() throws Exception {
3285         String[] allCameraIds = getAllCameraIds();
3286         for (int i = 0; i < allCameraIds.length; i++) {
3287             Log.i(TAG, "testDeviceStateOrientationMapCharacteristics: Testing camera ID " +
3288                     allCameraIds[i]);
3289 
3290             CameraCharacteristics c = mCharacteristics.get(i);
3291             DeviceStateSensorOrientationMap orientationMap = c.get(
3292                     CameraCharacteristics.INFO_DEVICE_STATE_SENSOR_ORIENTATION_MAP);
3293             if (orientationMap == null) {
3294                 continue;
3295             }
3296             // DeviceStateOrientationMaps must only be present within logical camera
3297             // characteristics.
3298             assertTrue("Camera id: " + i + " All devices advertising a " +
3299                     "DeviceStateSensorOrientationMap must also be logical cameras!",
3300                     CameraTestUtils.hasCapability(c,
3301                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA));
3302             List<Long> supportedStates = new ArrayList<>(Arrays.asList(
3303                     DeviceStateSensorOrientationMap.NORMAL, DeviceStateSensorOrientationMap.FOLDED));
3304             for (long deviceState : supportedStates) {
3305                 int orientation = orientationMap.getSensorOrientation(deviceState);
3306                 assertTrue("CameraId: " + i + " Unexpected orientation: " + orientation,
3307                         (orientation >= 0) && (orientation <= 270) &&
3308                         ((orientation % 90) == 0));
3309             }
3310         }
3311     }
3312 
3313     /**
3314      * Check that all devices available through the legacy API are also
3315      * accessible via Camera2.
3316      */
3317     @CddTest(requirement="7.5.4/C-0-11")
3318     @Test
testLegacyCameraDeviceParity()3319     public void testLegacyCameraDeviceParity() {
3320         if (mAdoptShellPerm) {
3321             // There is no current way to determine in camera1 api if a device is a system camera
3322             // Skip test, http://b/141496896
3323             return;
3324         }
3325         if (mOverrideCameraId != null) {
3326             // A single camera is being tested. Skip test.
3327             return;
3328         }
3329         int legacyDeviceCount = Camera.getNumberOfCameras();
3330         assertTrue("More legacy devices: " + legacyDeviceCount + " compared to Camera2 devices: " +
3331                 mCharacteristics.size(), legacyDeviceCount <= mCharacteristics.size());
3332 
3333         ArrayList<CameraCharacteristics> chars = new ArrayList<> (mCharacteristics);
3334         for (int i = 0; i < legacyDeviceCount; i++) {
3335             Camera camera = null;
3336             Camera.Parameters legacyParams = null;
3337             Camera.CameraInfo legacyInfo = new Camera.CameraInfo();
3338             try {
3339                 Camera.getCameraInfo(i, legacyInfo);
3340                 camera = Camera.open(i);
3341                 legacyParams = camera.getParameters();
3342 
3343                 assertNotNull("Camera parameters for device: " + i + "  must not be null",
3344                         legacyParams);
3345             } finally {
3346                 if (camera != null) {
3347                     camera.release();
3348                 }
3349             }
3350 
3351             // Camera Ids between legacy devices and Camera2 device could be
3352             // different try to match devices by using other common traits.
3353             CameraCharacteristics found = null;
3354             for (CameraCharacteristics ch : chars) {
3355                 if (matchParametersToCharacteristics(legacyParams, legacyInfo, ch)) {
3356                     found = ch;
3357                     break;
3358                 }
3359             }
3360             assertNotNull("No matching Camera2 device for legacy device id: " + i, found);
3361 
3362             chars.remove(found);
3363         }
3364     }
3365 
3366     /**
3367      * Check camera orientation against device orientation
3368      */
3369     @AppModeFull(reason = "DeviceStateManager is not accessible to instant apps")
3370     @CddTest(requirement = "7.5.5/C-1-1")
3371     @Test
testCameraOrientationAlignedWithDevice()3372     public void testCameraOrientationAlignedWithDevice() throws Exception {
3373         assumeFalse("Skip test: CDD 7.5.5/C-1-1 does not apply to automotive",
3374                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
3375         if (CameraUtils.isDeviceFoldable(mContext)) {
3376             // CDD 7.5.5/C-1-1 does not apply to devices with folding displays as the display aspect
3377             // ratios might change with the device's folding state.
3378             // Skip this test in foldables until the CDD is updated to include foldables.
3379             Log.i(TAG, "CDD 7.5.5/C-1-1 does not apply to foldables, skipping"
3380                     + " testCameraOrientationAlignedWithDevice");
3381             return;
3382         }
3383 
3384         // Start the empty activty to check the display we're testing.
3385         mActivityRule.launchActivity(new Intent());
3386         Context foregroundActivity = mActivityRule.getActivity();
3387 
3388         WindowManager windowManager = foregroundActivity.getSystemService(WindowManager.class);
3389         assertNotNull("Could not get window manager for test activity.", windowManager);
3390 
3391         WindowMetrics metrics = windowManager.getMaximumWindowMetrics();
3392         Rect displayBounds = metrics.getBounds();
3393         int widthPixels = displayBounds.width();
3394         int heightPixels = displayBounds.height();
3395 
3396         // For square screen, test is guaranteed to pass
3397         if (widthPixels == heightPixels) {
3398             return;
3399         }
3400 
3401         // Handle display rotation
3402         Display display = foregroundActivity.getDisplay();
3403         int displayRotation = display.getRotation();
3404         if (displayRotation == Surface.ROTATION_90 || displayRotation == Surface.ROTATION_270) {
3405             int tmp = widthPixels;
3406             widthPixels = heightPixels;
3407             heightPixels = tmp;
3408         }
3409         boolean isDevicePortrait = widthPixels < heightPixels;
3410 
3411         String[] allCameraIds = getAllCameraIds();
3412         for (int i = 0; i < allCameraIds.length; i++) {
3413             CameraCharacteristics c = mCharacteristics.get(i);
3414             // Camera size
3415             Size pixelArraySize = c.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
3416             // Camera orientation
3417             int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
3418 
3419             // For square sensor, test is guaranteed to pass
3420             if (pixelArraySize.getWidth() == pixelArraySize.getHeight()) {
3421                 continue;
3422             }
3423 
3424             Integer facing = c.get(CameraCharacteristics.LENS_FACING);
3425             if (facing == CameraCharacteristics.LENS_FACING_EXTERNAL) {
3426                 // CDD "7.5.5/C-1-1" only applies to front and back cameras
3427                 continue;
3428             }
3429             // Camera size adjusted for device native orientation.
3430             Size adjustedSensorSize;
3431             if (sensorOrientation == 90 || sensorOrientation == 270) {
3432                 adjustedSensorSize = new Size(
3433                         pixelArraySize.getHeight(), pixelArraySize.getWidth());
3434             } else {
3435                 adjustedSensorSize = pixelArraySize;
3436             }
3437 
3438             boolean isCameraPortrait =
3439                     adjustedSensorSize.getWidth() < adjustedSensorSize.getHeight();
3440 
3441             // device and camera orientation should either be both portrait, or both landscape
3442             assertEquals("Camera " + allCameraIds[i] + "'s long dimension must "
3443                     + "align with screen's long dimension", isDevicePortrait, isCameraPortrait);
3444         }
3445     }
3446 
3447     /**
3448      * Check the CameraX Night extension requirement
3449      */
3450     private ExtensionsManager getCameraXExtensionManager() throws Exception {
3451         // Obtain an instance of a process camera provider
3452         final ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
3453                 ProcessCameraProvider.getInstance(mContext);
3454         ProcessCameraProvider cameraProvider = null;
3455         try {
3456             cameraProvider = cameraProviderFuture.get(WAIT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS);
3457         } catch (InterruptedException | ExecutionException | TimeoutException e) {
3458             throw new AssertionError("Provider future failed to complete", e);
3459         }
3460         assertNotNull("CameraProviderManager isn't available", cameraProvider);
3461 
3462         // Obtain an instance of the extension manager
3463         final ListenableFuture<ExtensionsManager> extensionsManagerFuture =
3464                 ExtensionsManager.getInstanceAsync(mContext, cameraProvider);
3465         ExtensionsManager extensionsManager = null;
3466         try {
3467             extensionsManager = extensionsManagerFuture.get(
3468                     WAIT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS);
3469         } catch (InterruptedException | ExecutionException | TimeoutException e) {
3470             throw new AssertionError("ExtensionManager future failed to complete", e);
3471         }
3472         assertNotNull("ExtensionManager isn't available", extensionsManager);
3473 
3474         return extensionsManager;
3475     }
3476 
3477     /**
3478      * Check camera characteristics for Performance class requirements as specified
3479      * in CDD camera section 7.5
3480      */
3481     @Test
3482     @AppModeFull(reason = "DeviceStateManager is not accessible to instant apps")
3483     @CddTest(requirements = {
3484             "2.2.7.2/7.5/H-1-1",
3485             "2.2.7.2/7.5/H-1-2",
3486             "2.2.7.2/7.5/H-1-3",
3487             "2.2.7.2/7.5/H-1-4",
3488             "2.2.7.2/7.5/H-1-8",
3489             "2.2.7.2/7.5/H-1-9",
3490             "2.2.7.2/7.5/H-1-10",
3491             "2.2.7.2/7.5/H-1-11",
3492             "2.2.7.2/7.5/H-1-12",
3493             "2.2.7.2/7.5/H-1-13",
3494             "2.2.7.2/7.5/H-1-14"})
3495     public void testCameraPerfClassCharacteristics() throws Exception {
3496         assumeFalse("Media performance class tests not applicable if shell permission is adopted",
3497                 mAdoptShellPerm);
3498         assumeTrue("Media performance class tests not applicable when test is restricted "
3499                 + "to single camera by specifying camera id override.", mOverrideCameraId == null);
3500 
3501         PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
3502         PrimaryRearCameraResolutionAndFrameRateRequirement primaryRearReq =
3503                 Requirements.addR7_5__H_1_1().to(pce);
3504         PrimaryFrontCameraResolutionAndFrameRateRequirement primaryFrontReq =
3505                 Requirements.addR7_5__H_1_2().to(pce);
3506         CameraHardwareLevelRequirement hwLevelReq =
3507                 Requirements.addR7_5__H_1_3().to(pce);
3508         TimestampSourceRealtimeRequirement timestampSourceReq =
3509                 Requirements.addR7_5__H_1_4().to(pce);
3510         CameraRAWCapabilityRequirement rearRawReq =
3511                 Requirements.addR7_5__H_1_8().to(pce);
3512         CameraSlowMotionRequirement hfrReq =
3513                 Requirements.addR7_5__H_1_9().to(pce);
3514         CameraUltrawideZoomRatioRequirement ultrawideZoomRatioReq =
3515                 Requirements.addR7_5__H_1_10().to(pce);
3516         CameraConcurrentRearFrontStreamingRequirement concurrentRearFrontReq =
3517                 Requirements.addR7_5__H_1_11().to(pce);
3518         CameraPreviewStabilizationRequirement previewStabilizationReq =
3519                 Requirements.addR7_5__H_1_12().to(pce);
3520         CameraLogicalMultiCameraRequirement logicalMultiCameraReq =
3521                 Requirements.addR7_5__H_1_13().to(pce);
3522         CameraStreamUseCaseRequirement streamUseCaseReq =
3523                 Requirements.addR7_5__H_1_14().to(pce);
3524 
3525         String primaryRearId = null;
3526         String primaryFrontId = null;
3527         String[] cameraIdsUnderTest = getCameraIdsUnderTest();
3528         for (int i = 0; i < cameraIdsUnderTest.length; i++) {
3529             String cameraId = cameraIdsUnderTest[i];
3530             boolean isPrimaryRear = CameraTestUtils.isPrimaryRearFacingCamera(
3531                     mCameraManager, cameraId);
3532             boolean isPrimaryFront = CameraTestUtils.isPrimaryFrontFacingCamera(
3533                     mCameraManager, cameraId);
3534             if (!isPrimaryRear && !isPrimaryFront) {
3535                 continue;
3536             }
3537 
3538             CameraCharacteristics c = mCharacteristics.get(i);
3539             StaticMetadata staticInfo = mAllStaticInfo.get(cameraId);
3540 
3541             // H-1-1, H-1-2
3542             Size pixelArraySize = CameraTestUtils.getValueNotNull(
3543                     c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
3544             long sensorResolution = pixelArraySize.getHeight() * pixelArraySize.getWidth();
3545             StreamConfigurationMap config = staticInfo.getValueFromKeyNonNull(
3546                     CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
3547             assertNotNull("No stream configuration map found for ID " + cameraId, config);
3548             List<Size> videoSizes = CameraTestUtils.getSupportedVideoSizes(cameraId,
3549                     mCameraManager, null /*bound*/);
3550 
3551             Integer timestampSource = c.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
3552             if (isPrimaryRear) {
3553                 primaryRearId = cameraId;
3554                 primaryRearReq.setPrimaryCameraAvailable(true);
3555                 primaryRearReq.setPrimaryCameraResolution(sensorResolution);
3556                 hwLevelReq.setRearPrimaryCameraHwlLevel(staticInfo.getHardwareLevelChecked());
3557                 timestampSourceReq.setRearPrimaryCameraTimestampSource(timestampSource);
3558 
3559                 // 4K @ 30fps
3560                 boolean supportUHD = videoSizes.contains(UHD);
3561                 boolean supportDC4K = videoSizes.contains(DC4K);
3562                 boolean support4K = (supportUHD || supportDC4K);
3563                 boolean supportFullHD = videoSizes.contains(FULLHD);
3564                 boolean support720p = videoSizes.contains(HD);
3565                 primaryRearReq.setPrimaryCameraVideoSizeReqSatisfied(support4K);
3566                 primaryRearReq.setPrimaryCamera720PVideoSizeReqSatisfied(support720p);
3567                 primaryRearReq.setPrimaryCamera1080PVideoSizeReqSatisfied(supportFullHD);
3568                 if (support4K) {
3569                     long minFrameDuration = config.getOutputMinFrameDuration(
3570                             android.media.MediaRecorder.class, supportDC4K ? DC4K : UHD);
3571                     primaryRearReq.setPrimaryCameraVideoFps(1e9 / minFrameDuration);
3572                 } else {
3573                     primaryRearReq.setPrimaryCameraVideoFps(-1);
3574                 }
3575                 if (supportFullHD) {
3576                     long minFrameDuration = config.getOutputMinFrameDuration(
3577                             android.media.MediaRecorder.class, FULLHD);
3578                     primaryRearReq.setPrimaryCamera1080PVideoFps(1e9 / minFrameDuration);
3579                 } else {
3580                     primaryRearReq.setPrimaryCamera1080PVideoFps(-1);
3581                 }
3582                 if (support720p) {
3583                     long minFrameDuration = config.getOutputMinFrameDuration(
3584                             android.media.MediaRecorder.class, HD);
3585                     primaryRearReq.setPrimaryCamera720PVideoFps(1e9 / minFrameDuration);
3586                 } else {
3587                     primaryRearReq.setPrimaryCamera720PVideoFps(-1);
3588                 }
3589                 // H-1-9
3590                 boolean supportHighSpeed = staticInfo.isCapabilitySupported(CONSTRAINED_HIGH_SPEED);
3591                 boolean support240Fps = false;
3592                 if (supportHighSpeed) {
3593                     Size[] availableHighSpeedSizes = config.getHighSpeedVideoSizes();
3594                     for (Size size : availableHighSpeedSizes) {
3595                         if (!size.equals(HD) && !size.equals(FULLHD)) {
3596                             continue;
3597                         }
3598                         Range<Integer>[] availableFpsRanges =
3599                                 config.getHighSpeedVideoFpsRangesFor(size);
3600                         for (Range<Integer> fpsRange : availableFpsRanges) {
3601                             if (fpsRange.getUpper() == 240) {
3602                                 support240Fps = true;
3603                                 break;
3604                             }
3605                         }
3606                         if (support240Fps) {
3607                             break;
3608                         }
3609                     }
3610                 }
3611                 hfrReq.setRearCamera240FpsSupported(support240Fps);
3612             } else {
3613                 primaryFrontId = cameraId;
3614                 primaryFrontReq.setPrimaryCameraAvailable(true);
3615                 primaryFrontReq.setPrimaryCameraResolution(sensorResolution);
3616                 hwLevelReq.setFrontPrimaryCameraHwlLevel(staticInfo.getHardwareLevelChecked());
3617                 timestampSourceReq.setFrontPrimaryCameraTimestampSource(timestampSource);
3618 
3619                 // 1080P @ 30fps
3620                 boolean supportFULLHD = videoSizes.contains(FULLHD);
3621                 primaryFrontReq.setPrimaryCameraVideoSizeReqSatisfied(supportFULLHD);
3622                 if (supportFULLHD) {
3623                     long minFrameDuration = config.getOutputMinFrameDuration(
3624                             android.media.MediaRecorder.class, FULLHD);
3625                     primaryFrontReq.setPrimaryCameraVideoFps(1e9 / minFrameDuration);
3626                 } else {
3627                     primaryFrontReq.setPrimaryCameraVideoFps(-1);
3628                 }
3629             }
3630 
3631             // H-1-8
3632             if (isPrimaryRear) {
3633                 boolean supportRaw = staticInfo.isCapabilitySupported(RAW);
3634                 rearRawReq.setRearCameraRawSupported(supportRaw);
3635             }
3636 
3637             // H-1-10
3638             final double FOV_THRESHOLD = 0.001f;
3639             double primaryToMaxFovRatio = getPrimaryToMaxFovRatio(cameraId, staticInfo);
3640             Range<Float> zoomRatioRange = staticInfo.getZoomRatioRangeChecked();
3641             boolean meetH110 = (CameraUtils.isDeviceFoldable(mContext)
3642                     && hasUndefinedPoseReferenceWithSameFacing(cameraId, staticInfo))
3643                     || (primaryToMaxFovRatio >= 1.0f - FOV_THRESHOLD)
3644                     || (zoomRatioRange.getLower() < 1.0f - FOV_THRESHOLD);
3645             if (isPrimaryRear) {
3646                 ultrawideZoomRatioReq.setRearCameraUltrawideZoomReqMet(meetH110);
3647             } else {
3648                 ultrawideZoomRatioReq.setFrontCameraUltrawideZoomReqMet(meetH110);
3649             }
3650 
3651             // H-1-12
3652             if (isPrimaryRear) {
3653                 boolean previewStab = staticInfo.isPreviewStabilizationSupported();
3654                 previewStabilizationReq.setRearCameraPreviewStabilizationSupported(previewStab);
3655             }
3656 
3657             // H-1-13
3658             if (isPrimaryRear) {
3659                 int facing = staticInfo.getLensFacingChecked();
3660                 int numOfPhysicalRgbCameras = getNumberOfRgbPhysicalCameras(facing);
3661                 boolean logicalMultiCameraReqMet =
3662                         (numOfPhysicalRgbCameras <= 1) || staticInfo.isLogicalMultiCamera();
3663                 logicalMultiCameraReq.setRearCameraLogicalMultiCameraReqMet(
3664                         logicalMultiCameraReqMet);
3665             }
3666 
3667             // H-1-14
3668             boolean streamUseCaseSupported = staticInfo.isStreamUseCaseSupported();
3669             if (isPrimaryRear) {
3670                 streamUseCaseReq.setRearCameraStreamUsecaseSupported(streamUseCaseSupported);
3671             } else {
3672                 streamUseCaseReq.setFrontCameraStreamUsecaseSupported(streamUseCaseSupported);
3673             }
3674         }
3675 
3676         if (primaryRearId == null) {
3677             primaryRearReq.setPrimaryCameraAvailable(false);
3678             primaryRearReq.setPrimaryCameraResolution(-1);
3679             primaryRearReq.setPrimaryCameraVideoSizeReqSatisfied(false);
3680             primaryRearReq.setPrimaryCameraVideoFps(-1);
3681             primaryRearReq.setPrimaryCamera1080PVideoFps(-1);
3682             primaryRearReq.setPrimaryCamera720PVideoFps(-1);
3683             primaryRearReq.setPrimaryCamera720PVideoSizeReqSatisfied(false);
3684             primaryRearReq.setPrimaryCamera1080PVideoSizeReqSatisfied(false);
3685             hwLevelReq.setRearPrimaryCameraHwlLevel(-1);
3686             timestampSourceReq.setRearPrimaryCameraTimestampSource(
3687                     CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN);
3688             rearRawReq.setRearCameraRawSupported(false);
3689             hfrReq.setRearCamera240FpsSupported(false);
3690             ultrawideZoomRatioReq.setRearCameraUltrawideZoomReqMet(false);
3691             previewStabilizationReq.setRearCameraPreviewStabilizationSupported(false);
3692             logicalMultiCameraReq.setRearCameraLogicalMultiCameraReqMet(false);
3693             streamUseCaseReq.setRearCameraStreamUsecaseSupported(false);
3694         }
3695         if (primaryFrontId == null) {
3696             primaryFrontReq.setPrimaryCameraAvailable(false);
3697             primaryFrontReq.setPrimaryCameraResolution(-1);
3698             primaryFrontReq.setPrimaryCameraVideoSizeReqSatisfied(false);
3699             primaryFrontReq.setPrimaryCameraVideoFps(-1);
3700             hwLevelReq.setFrontPrimaryCameraHwlLevel(-1);
3701             timestampSourceReq.setFrontPrimaryCameraTimestampSource(
3702                     CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN);
3703             ultrawideZoomRatioReq.setFrontCameraUltrawideZoomReqMet(false);
3704             streamUseCaseReq.setFrontCameraStreamUsecaseSupported(false);
3705         }
3706 
3707         // H-1-11
3708         Set<Set<String>> concurrentCameraIds = mCameraManager.getConcurrentCameraIds();
3709         Set<String> primaryCameras = new HashSet<>(Arrays.asList(primaryRearId, primaryFrontId));
3710         boolean supportPrimaryFrontBack = concurrentCameraIds.contains(primaryCameras);
3711         concurrentRearFrontReq.setRearFrontConcurrentCamera(supportPrimaryFrontBack);
3712 
3713         pce.submitAndCheck();
3714     }
3715 
3716     /**
3717      * Get the number of physical RGB camera devices facing the same direction as the
3718      * primary camera id
3719      */
3720     private int getNumberOfRgbPhysicalCameras(int facing) throws Exception {
3721         int numOfRgbPhysicalCameras = 0;
3722         for (String id : getAllCameraIds()) {
3723             StaticMetadata staticInfo = mAllStaticInfo.get(id);
3724             if (staticInfo.getLensFacingChecked() != facing) {
3725                 continue;
3726             }
3727             if (staticInfo.isLogicalMultiCamera()) {
3728                 continue;
3729             }
3730             if (!staticInfo.isColorOutputSupported()) {
3731                 continue;
3732             }
3733             if (staticInfo.isMonochromeCamera()) {
3734                 continue;
3735             }
3736             numOfRgbPhysicalCameras++;
3737         }
3738         return numOfRgbPhysicalCameras;
3739     }
3740 
3741     /**
3742      * Get the ratio of FOV between the primary camera and the maximium FOV of all color cameras
3743      * of the same facing.
3744      */
3745     private double getPrimaryToMaxFovRatio(String primaryCameraId, StaticMetadata staticInfo)
3746                 throws Exception {
3747         int facing = staticInfo.getLensFacingChecked();
3748         double fovForPrimaryCamera = getCameraFov(staticInfo);
3749 
3750         double largestFov = fovForPrimaryCamera;
3751         for (String id : getAllCameraIds()) {
3752             if (primaryCameraId.equals(id)) {
3753                 continue;
3754             }
3755 
3756             StaticMetadata staticInfoForId = mAllStaticInfo.get(id);
3757             if (staticInfoForId.getLensFacingChecked() != facing) {
3758                 continue;
3759             }
3760             if (!staticInfoForId.isColorOutputSupported()) {
3761                 continue;
3762             }
3763 
3764             largestFov = Math.max(largestFov, getCameraFov(staticInfoForId));
3765         }
3766 
3767         Log.v(TAG, "Primary camera " + primaryCameraId + " FOV is " + fovForPrimaryCamera
3768                 + ", largest camera FOV for the same facing is " + largestFov);
3769         return fovForPrimaryCamera / largestFov;
3770     }
3771 
3772     private double getCameraFov(StaticMetadata staticInfo) {
3773         SizeF physicalSize = staticInfo.getCharacteristics().get(
3774                 CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE);
3775         double physicalDiag = Math.sqrt(Math.pow(physicalSize.getWidth(), 2)
3776                 + Math.pow(physicalSize.getHeight(), 2));
3777         float[] availableFocalLengths = staticInfo.getAvailableFocalLengthsChecked();
3778 
3779         return 2 * Math.toDegrees(Math.atan2(physicalDiag / 2, availableFocalLengths[0]));
3780     }
3781 
3782     /**
3783      * Whether there is a camera with UNDEFINED lens pose reference with the same facing.
3784      */
3785     private boolean hasUndefinedPoseReferenceWithSameFacing(String cameraId,
3786             StaticMetadata staticInfo) throws Exception {
3787         int facing = staticInfo.getLensFacingChecked();
3788         for (String id : getAllCameraIds()) {
3789             if (cameraId.equals(id)) {
3790                 continue;
3791             }
3792             StaticMetadata staticInfoForId = mAllStaticInfo.get(id);
3793             if (staticInfoForId.getLensFacingChecked() != facing) {
3794                 continue;
3795             }
3796             if (!staticInfoForId.isColorOutputSupported()) {
3797                 continue;
3798             }
3799             if (staticInfoForId.isPoseReferenceUndefined()) {
3800                 return true;
3801             }
3802         }
3803         return false;
3804     }
3805 
3806     /**
3807      * Check camera characteristics for Android 14 Performance class requirements
3808      * as specified in CDD camera section 7.5
3809      */
3810     @Test
3811     @AppModeFull(reason = "Media Performance class test not applicable to instant apps")
3812     @CddTest(requirements = {
3813             "2.2.7.2/7.5/H-1-16",
3814             "2.2.7.2/7.5/H-1-17"})
3815     public void testCameraUPerfClassCharacteristics() throws Exception {
3816         assumeFalse("Media performance class tests not applicable if shell permission is adopted",
3817                 mAdoptShellPerm);
3818         assumeTrue("Media performance class tests not applicable when test is restricted "
3819                 + "to single camera by specifying camera id override.", mOverrideCameraId == null);
3820 
3821         PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
3822         CameraDynamicRange10BitRequirement dynamicRangeTenBitsReq =
3823                 Requirements.addR7_5__H_1_16().to(pce);
3824         CameraFaceDetectionRequirement faceDetectionReq =
3825                 Requirements.addR7_5__H_1_17().to(pce);
3826 
3827         String primaryRearId = CameraTestUtils.getPrimaryRearCamera(mCameraManager,
3828                 getCameraIdsUnderTest());
3829         String primaryFrontId = CameraTestUtils.getPrimaryFrontCamera(mCameraManager,
3830                 getCameraIdsUnderTest());
3831 
3832         // H-1-16
3833         verifyDynamicRangeTenBits(primaryRearId, true /* isRear */, dynamicRangeTenBitsReq);
3834         verifyDynamicRangeTenBits(primaryFrontId, false /* isRear */, dynamicRangeTenBitsReq);
3835 
3836         // H-1-17
3837         verifyFaceDetection(primaryRearId, true /* isRear */, faceDetectionReq);
3838         verifyFaceDetection(primaryFrontId, false /* isRear */, faceDetectionReq);
3839 
3840         pce.submitAndCheck();
3841     }
3842 
3843     @Test
3844     @AppModeFull(reason = "Media Performance class test not applicable to instant apps")
3845     @CddTest(requirements = {"2.2.7.2/7.5/H-1-18"})
3846     public void testCameraVPerfClassCharacteristics() throws Exception {
3847         assumeFalse("Media performance class tests not applicable if shell permission is adopted",
3848                 mAdoptShellPerm);
3849         assumeTrue("Media performance class tests not applicable when test is restricted "
3850                 + "to single camera by specifying camera id override.", mOverrideCameraId == null);
3851 
3852         PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
3853         CameraJPEGRRequirement jpegRReq = Requirements.addR7_5__H_1_18().to(pce);
3854 
3855         String primaryRearId = CameraTestUtils.getPrimaryRearCamera(mCameraManager,
3856                 getCameraIdsUnderTest());
3857         String primaryFrontId = CameraTestUtils.getPrimaryFrontCamera(mCameraManager,
3858                 getCameraIdsUnderTest());
3859 
3860         // H-1-18
3861         verifyJpegRRequirement(primaryRearId, true /* isRear */, jpegRReq);
3862         verifyJpegRRequirement(primaryFrontId, false /* isRear */, jpegRReq);
3863 
3864         pce.submitAndCheck();
3865     }
3866 
3867     /**
3868      * Check camera extension characteristics for Android 14 Performance class requirements
3869      * as specified in CDD camera section 7.5
3870      */
3871     @Test
3872     @AppModeFull(reason = "Media Performance class test not applicable to instant apps")
3873     @CddTest(requirements = {
3874             "2.2.7.2/7.5/H-1-15"})
3875     public void testCameraUPerfClassExtensionCharacteristics() throws Exception {
3876         assumeFalse("Media performance class tests not applicable if shell permission is adopted",
3877                 mAdoptShellPerm);
3878         assumeTrue("Media performance class tests not applicable when test is restricted "
3879                 + "to single camera by specifying camera id override.", mOverrideCameraId == null);
3880 
3881         PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
3882         CameraNightModeExtensionRequirement cameraExtensionReq =
3883                 Requirements.addR7_5__H_1_15().to(pce);
3884 
3885         String primaryRearId = CameraTestUtils.getPrimaryRearCamera(mCameraManager,
3886                 getCameraIdsUnderTest());
3887         String primaryFrontId = CameraTestUtils.getPrimaryFrontCamera(mCameraManager,
3888                 getCameraIdsUnderTest());
3889 
3890         verifyExtensionForCamera(primaryRearId, true /* isRear */, cameraExtensionReq);
3891         verifyExtensionForCamera(primaryFrontId, false /* isRear */, cameraExtensionReq);
3892 
3893         pce.submitAndCheck();
3894     }
3895 
3896     /**
3897      * Verify camera2 and CameraX extension requirements for a camera id
3898      */
3899     private void verifyExtensionForCamera(String cameraId, boolean isRear,
3900             CameraNightModeExtensionRequirement req) throws Exception {
3901         if (cameraId == null) {
3902             if (isRear) {
3903                 req.setRearCamera2ExtensionNightSupported(false);
3904                 req.setRearCameraxExtensionNightSupported(false);
3905             } else {
3906                 req.setFrontCamera2ExtensionNightSupported(false);
3907                 req.setFrontCameraxExtensionNightSupported(false);
3908             }
3909             return;
3910         }
3911 
3912         ExtensionsManager extensionsManager = getCameraXExtensionManager();
3913         CameraExtensionCharacteristics extensionChars =
3914                 mCameraManager.getCameraExtensionCharacteristics(cameraId);
3915         StaticMetadata staticInfo = mAllStaticInfo.get(cameraId);
3916 
3917         List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
3918         boolean nightExtensionSupported =
3919                 supportedExtensions.contains(CameraExtensionCharacteristics.EXTENSION_NIGHT);
3920         if (isRear) {
3921             req.setRearCamera2ExtensionNightSupported(nightExtensionSupported);
3922         } else {
3923             req.setFrontCamera2ExtensionNightSupported(nightExtensionSupported);
3924         }
3925 
3926         CameraSelector selector = isRear
3927                 ? CameraSelector.DEFAULT_BACK_CAMERA
3928                 : CameraSelector.DEFAULT_FRONT_CAMERA;
3929         nightExtensionSupported =
3930                 extensionsManager.isExtensionAvailable(selector, ExtensionMode.NIGHT);
3931         if (isRear) {
3932             req.setRearCameraxExtensionNightSupported(nightExtensionSupported);
3933         } else {
3934             req.setFrontCameraxExtensionNightSupported(nightExtensionSupported);
3935         }
3936     }
3937 
3938     /**
3939      * Verify JPEG_R requirement for a camera id
3940      */
3941     private void verifyJpegRRequirement(String cameraId, boolean isRear, CameraJPEGRRequirement req)
3942             throws Exception {
3943         if (cameraId == null) {
3944             if (isRear) {
3945                 req.setPrimaryRearCameraJpegRSupported(false);
3946             } else {
3947                 req.setPrimaryFrontCameraJpegRSupported(false);
3948             }
3949             return;
3950         }
3951 
3952         StaticMetadata staticInfo = mAllStaticInfo.get(cameraId);
3953 
3954         if (isRear) {
3955             req.setPrimaryRearCameraJpegRSupported(staticInfo.isJpegRSupported());
3956         } else {
3957             req.setPrimaryFrontCameraJpegRSupported(staticInfo.isJpegRSupported());
3958         }
3959     }
3960 
3961     /**
3962      * Verify dynamic range ten bits requirement for a camera id
3963      */
3964     private void verifyDynamicRangeTenBits(String cameraId, boolean isRear,
3965             CameraDynamicRange10BitRequirement req) throws Exception {
3966         if (cameraId == null) {
3967             if (isRear) {
3968                 req.setRearCameraDynamicTenbitsSupported(false);
3969             } else {
3970                 req.setFrontCameraDynamicTenbitsSupported(false);
3971             }
3972             return;
3973         }
3974 
3975         StaticMetadata staticInfo = mAllStaticInfo.get(cameraId);
3976         boolean dynamicRangeTenBitsSupported =
3977                 staticInfo.isCapabilitySupported(DYNAMIC_RANGE_TEN_BIT);
3978 
3979         if (isRear) {
3980             req.setRearCameraDynamicTenbitsSupported(dynamicRangeTenBitsSupported);
3981         } else {
3982             req.setFrontCameraDynamicTenbitsSupported(dynamicRangeTenBitsSupported);
3983         }
3984     }
3985 
3986     /**
3987      * Verify face detection requirements for a camera id
3988      */
3989     private void verifyFaceDetection(String cameraId, boolean isRear,
3990                                      CameraFaceDetectionRequirement req) {
3991         if (cameraId == null) {
3992             if (isRear) {
3993                 req.setRearCameraFaceDetectionSupported(false);
3994             } else {
3995                 req.setFrontCameraFaceDetectionSupported(false);
3996             }
3997             return;
3998         }
3999 
4000         StaticMetadata staticInfo = mAllStaticInfo.get(cameraId);
4001         int[] availableFaceDetectionModes = staticInfo.getAvailableFaceDetectModesChecked();
4002         assertNotNull(availableFaceDetectionModes);
4003         int[] supportedFaceDetectionModes = {FACE_DETECTION_MODE_SIMPLE, FACE_DETECTION_MODE_FULL};
4004         boolean faceDetectionSupported = arrayContainsAnyOf(availableFaceDetectionModes,
4005                 supportedFaceDetectionModes);
4006 
4007         if (isRear) {
4008             req.setRearCameraFaceDetectionSupported(faceDetectionSupported);
4009         } else {
4010             req.setFrontCameraFaceDetectionSupported(faceDetectionSupported);
4011         }
4012     }
4013 
4014     /**
4015      * Get lens distortion coefficients, as a list of 6 floats; returns null if no valid
4016      * distortion field is available
4017      */
4018     private float[] getLensDistortion(CameraCharacteristics c) {
4019         float[] distortion = null;
4020         float[] newDistortion = c.get(CameraCharacteristics.LENS_DISTORTION);
4021         if (Build.VERSION.DEVICE_INITIAL_SDK_INT > Build.VERSION_CODES.O_MR1 || newDistortion != null) {
4022             // New devices need to use fixed radial distortion definition; old devices can
4023             // opt-in to it
4024             if (newDistortion != null && newDistortion.length == 5) {
4025                 distortion = new float[6];
4026                 distortion[0] = 1.0f;
4027                 for (int i = 1; i < 6; i++) {
4028                     distortion[i] = newDistortion[i-1];
4029                 }
4030             }
4031         } else {
4032             // Select old field only if on older first SDK and new definition not available
4033             distortion = c.get(CameraCharacteristics.LENS_RADIAL_DISTORTION);
4034         }
4035         return distortion;
4036     }
4037 
4038     /**
4039      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
4040      */
4041     private Size findInvalidSize(Size[] goodSizes) {
4042         return findInvalidSize(Arrays.asList(goodSizes));
4043     }
4044 
4045     /**
4046      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
4047      */
4048     private Size findInvalidSize(List<Size> goodSizes) {
4049         Size invalidSize = new Size(goodSizes.get(0).getWidth() + 1, goodSizes.get(0).getHeight());
4050         while(goodSizes.contains(invalidSize)) {
4051             invalidSize = new Size(invalidSize.getWidth() + 1, invalidSize.getHeight());
4052         }
4053         return invalidSize;
4054     }
4055 
4056     /**
4057      * Validate {@link CameraCharacteristics#LENS_POSE_TRANSLATION} and @{link
4058      * CameraCharacteristics#LENS_POSE_ROTATION} of camera that list below characteristics in their
4059      * static metadata.
4060      * - CameraCharacteristics.AUTOMOTIVE_LOCATION_INTERIOR_OTHER
4061      * - CameraCharacteristics.AUTOMOTIVE_LOCATION_EXTERIOR_OTHER
4062      * - CameraCharacteristics.AUTOMOTIVE_LOCATION_EXTRA_OTHER
4063      */
4064     @Test
4065     public void testAutomotiveCameraCharacteristics() throws Exception {
4066         String[] allCameraIds = getAllCameraIds();
4067         for (int i = 0; i < allCameraIds.length; i++) {
4068             CameraCharacteristics c = mCharacteristics.get(i);
4069 
4070             Integer location = c.get(CameraCharacteristics.AUTOMOTIVE_LOCATION);
4071             int[] lensFacing = c.get(CameraCharacteristics.AUTOMOTIVE_LENS_FACING);
4072             if (location == null || lensFacing == null) {
4073                 // CameraManagerTest#testCameraManagerAutomotiveCameras() guarantees
4074                 // CameraCharacteristics.AUTOMOTIVE_LOCATION and
4075                 // CameraCharacteristics.AUTOMOTIVE_LENS_FACING are listed in a static metadata of
4076                 // cameras on the automotive device implementations.
4077                 continue;
4078             }
4079 
4080             float[] translation = c.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
4081             float[] rotation = c.get(CameraCharacteristics.LENS_POSE_ROTATION);
4082             assertTrue("android.lens.poseTranslation and android.lens.poseRotation must exist " +
4083                     "together or not at all",
4084                     (translation != null) == (rotation != null));
4085             if (translation == null && rotation == null) {
4086                 // Cameras without android.lens.poseTranslation and anroid.lens.poseRotation are
4087                 // exempt from this test case.
4088                 continue;
4089             }
4090 
4091             // android.lens.poseTranslation describes the lens optical center of the camera device
4092             // as a three dimensional vector (x, y, z) and in the unit of meters.  On the automotive
4093             // sensor coordinate system, we expect the following:
4094             // - The width of the vehicle body frame would not exceed 6 meters, which is a width of
4095             //   the vehicle lane approximately.
4096             // - The length of the vehicle body frame would not exceed 10 meters, which is an
4097             //   average length of the city bus.  We apply approximately 20% tolerance to this value
4098             //   because of a relatively higher variance of the vehicle's length.
4099             // - The height of the vehicle body frame would not exceed 5 meters, which is an average
4100             //   height of the double decker bus.
4101             assertTrue("Lens pose translation vector is invalid",
4102                     (translation[0] >= -3 && translation[0] <= 3)
4103                             && (translation[1] >= -2 && translation[1] <= 10)
4104                             && (translation[2] >= 0 && translation[2] <= 5));
4105 
4106             // Convert a given quaternion to axis-angle representation
4107             double theta = 2.0 * Math.acos(rotation[3]);
4108             double a_x = rotation[0] / Math.sin(theta / 2.0);
4109             double a_y = rotation[1] / Math.sin(theta / 2.0);
4110             double a_z = rotation[2] / Math.sin(theta / 2.0);
4111 
4112             // Calculate an angle between a translation vector and a rotation axis
4113             double dot = (translation[0] * a_x) + (translation[1] * a_y) + (translation[2] * a_z);
4114             double mag_a = Math.sqrt(Math.pow(translation[0], 2) + Math.pow(translation[1], 2)
4115                     + Math.pow(translation[2], 2));
4116             double mag_b =
4117                     Math.sqrt(Math.pow(a_x, 2) + Math.pow(a_y, 2) + Math.pow(a_z, 2));
4118             double angle = Math.acos(dot / (mag_a * mag_b));
4119 
4120             if (location == CameraCharacteristics.AUTOMOTIVE_LOCATION_EXTERIOR_OTHER
4121                     || location == CameraCharacteristics.AUTOMOTIVE_LOCATION_EXTRA_OTHER) {
4122                 // If android.automotive.location is
4123                 // CameraCharacteristics.AUTOMOTIVE_LOCATION_EXTERIOR_OTHER or
4124                 // CameraCharacteristics.AUTOMOTIVE_LOCATION_EXTRA_OTHER, its
4125                 // android.lens.poseRotation should not describe a direction toward the inside of
4126                 // the vehicle cabin.
4127                 assertTrue("Lens pose rotation should not describe a direction toward the cabin",
4128                         angle >= Math.PI / 4);
4129             } else if (location == CameraCharacteristics.AUTOMOTIVE_LOCATION_INTERIOR) {
4130                 // Likewise, if android.automotive.lens.facing is
4131                 // CameraCharacteristics.AUTOMOTIVE_LENS_FACING_INTERIOR_OTHER, its
4132                 // android.lens.poseRotation should not describe a direction toward the outside
4133                 // of the vehicle cabin.
4134                 for (int value : lensFacing) {
4135                     if (value != CameraCharacteristics.AUTOMOTIVE_LENS_FACING_INTERIOR_OTHER) {
4136                         continue;
4137                     }
4138 
4139                     assertTrue("Lens pose rotation should not describe a direction toward " +
4140                             "the outside of the cabin",
4141                             angle <= Math.PI * 3 / 4);
4142 
4143                     // No need to examine remaining lens facing values.
4144                     break;
4145                 }
4146             }
4147         }
4148     }
4149 
4150     private void testLandscapeToPortraitSensorOrientation(String cameraId) throws Exception {
4151         CameraCharacteristics characteristics =
4152                 mCameraManager.getCameraCharacteristics(cameraId, false);
4153         CameraCharacteristics characteristicsOverride =
4154                 mCameraManager.getCameraCharacteristics(cameraId, true);
4155         int sensorOrientation = characteristics.get(
4156                 CameraCharacteristics.SENSOR_ORIENTATION);
4157         int sensorOrientationOverride = characteristicsOverride.get(
4158                 CameraCharacteristics.SENSOR_ORIENTATION);
4159 
4160         if (sensorOrientation == 0 || sensorOrientation == 180) {
4161             int facing = characteristics.get(CameraCharacteristics.LENS_FACING);
4162             if (facing == CameraMetadata.LENS_FACING_FRONT) {
4163                 assertEquals("SENSOR_ORIENTATION should be rotated 90 degrees"
4164                         + " counter-clockwise for front-facing cameras.",
4165                         (360 + sensorOrientation - 90) % 360, sensorOrientationOverride);
4166             } else if (facing == CameraMetadata.LENS_FACING_BACK) {
4167                 assertEquals("SENSOR_ORIENTATION should be rotated 90 degrees clockwise"
4168                         + " for back-facing cameras.",
4169                         (360 + sensorOrientation + 90) % 360, sensorOrientationOverride);
4170             } else {
4171                 assertEquals("SENSOR_ORIENTATION should be unchanged for external cameras.",
4172                         sensorOrientation, sensorOrientationOverride);
4173             }
4174         } else {
4175             assertEquals("SENSOR_ORIENTATION should be unchanged for non-landscape "
4176                     + "sensors.", sensorOrientation, sensorOrientationOverride);
4177         }
4178     }
4179 
4180     /**
4181      * Test that the landscape to portrait override modifies SENSOR_ORIENTATION as expected.
4182      * All cameras with SENSOR_ORIENTATION 0 or 180 should have SENSOR_ORIENTATION 90 or 270
4183      * when the override is turned on. Cameras not accessible via openCamera ("constituent
4184      * cameras") should not update their SENSOR_ORIENTATION values.
4185      */
4186     @Test
4187     public void testLandscapeToPortraitOverride() throws Exception {
4188         String[] cameraIdArr = mCameraManager.getCameraIdListNoLazy();
4189         ArrayList<String> cameraIdList = new ArrayList<>(Arrays.asList(cameraIdArr));
4190         for (String cameraId : getCameraIdsUnderTest()) {
4191             Log.i(TAG, "testLandscapeToPortraitOverride: Testing camera ID " + cameraId);
4192             StaticMetadata staticMetadata = mAllStaticInfo.get(cameraId);
4193 
4194             testLandscapeToPortraitSensorOrientation(cameraId);
4195 
4196             if (staticMetadata.isLogicalMultiCamera()) {
4197                 Log.i(TAG, "Camera " + cameraId + " is a logical multi-camera.");
4198 
4199                 CameraCharacteristics characteristics =
4200                         mCameraManager.getCameraCharacteristics(cameraId, false);
4201 
4202                 Set<String> physicalCameraIds = characteristics.getPhysicalCameraIds();
4203                 for (String physicalId : physicalCameraIds) {
4204                     if (!cameraIdList.contains(physicalId)) {
4205                         Log.i(TAG, "Testing constituent camera id: " + physicalId);
4206 
4207                         CameraCharacteristics physicalCharacteristics =
4208                                 mCameraManager.getCameraCharacteristics(physicalId, false);
4209                         CameraCharacteristics physicalCharacteristicsOverride =
4210                                 mCameraManager.getCameraCharacteristics(physicalId, true);
4211                         int physicalSensorOrientation = physicalCharacteristics.get(
4212                                 CameraCharacteristics.SENSOR_ORIENTATION);
4213                         int physicalSensorOrientationOverride = physicalCharacteristicsOverride.get(
4214                                 CameraCharacteristics.SENSOR_ORIENTATION);
4215 
4216                         // Check that physical camera orientations have NOT been overridden.
4217                         assertEquals("SENSOR_ORIENTATION should be unchanged for constituent "
4218                                 + "physical cameras.", physicalSensorOrientation,
4219                                 physicalSensorOrientationOverride);
4220                     }
4221                 }
4222             }
4223         }
4224     }
4225 
4226     /**
4227      * Validate that the rear/world facing cameras in automotive devices are oriented so that the
4228      * long dimension of the camera aligns with the X-Y plane of Android automotive sensor axes.
4229      */
4230     @CddTest(requirements = "7.5/A-1-1")
4231     @Test
4232     public void testAutomotiveCameraOrientation() throws Exception {
4233         assumeTrue(mContext.getPackageManager().hasSystemFeature(
4234                 PackageManager.FEATURE_AUTOMOTIVE));
4235         String[] allCameraIds = getAllCameraIds();
4236         for (int i = 0; i < allCameraIds.length; i++) {
4237             CameraCharacteristics c = mCharacteristics.get(i);
4238             int facing = c.get(CameraCharacteristics.LENS_FACING);
4239             if (facing == CameraMetadata.LENS_FACING_BACK) {
4240                 // Camera size
4241                 Size pixelArraySize = c.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
4242                 // Camera orientation
4243                 int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
4244                 // For square sensor, test is guaranteed to pass.
4245                 if (pixelArraySize.getWidth() == pixelArraySize.getHeight()) {
4246                     continue;
4247                 }
4248                 // Camera size adjusted for device native orientation.
4249                 Size adjustedSensorSize;
4250                 if (sensorOrientation == 90 || sensorOrientation == 270) {
4251                     adjustedSensorSize = new Size(
4252                             pixelArraySize.getHeight(), pixelArraySize.getWidth());
4253                 } else {
4254                     adjustedSensorSize = pixelArraySize;
4255                 }
4256                 boolean isCameraLandscape =
4257                         adjustedSensorSize.getWidth() > adjustedSensorSize.getHeight();
4258                 // Automotive camera orientation should be landscape for rear/world facing camera.
4259                 assertTrue("Automotive camera "  + allCameraIds[i] + " which is rear/world facing"
4260                         + " must align with the X-Y plane of Android automotive sensor axes",
4261                         isCameraLandscape);
4262             }
4263         }
4264     }
4265 
4266     /**
4267      * Validate the operating luminance range for low light boost. If the luminance range is
4268      * defined then the AE mode CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY must also
4269      * be present in the list of available AE modes.
4270      */
4271     @Test
4272     @RequiresFlagsEnabled(Flags.FLAG_CAMERA_AE_MODE_LOW_LIGHT_BOOST)
4273     public void testLowLightBoostLuminanceRange() throws Exception {
4274         String[] allCameraIds = getAllCameraIds();
4275         for (int i = 0; i < allCameraIds.length; i++) {
4276             Log.i(TAG, "testLowLightBoostLuminanceRange: Testing camera ID " + allCameraIds[i]);
4277             CameraCharacteristics c = mCharacteristics.get(i);
4278 
4279             // Check for the presence of AE mode low light boost
4280             int[] availableAeModes = c.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
4281             if (availableAeModes == null) {
4282                 Log.i(TAG, "Camera id " + allCameraIds[i] + " does not have AE modes. "
4283                         + "Skipping testLowLightBoostLuminanceRange");
4284                 continue;
4285             }
4286 
4287             assertNotNull("CONTROL_AE_AVAILABLE_MODES must be present", availableAeModes);
4288             boolean containsAeModeLowLightBoost = false;
4289             for (int aeMode : availableAeModes) {
4290                 if (aeMode
4291                         == CameraMetadata.CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY) {
4292                     containsAeModeLowLightBoost = true;
4293                     break;
4294                 }
4295             }
4296 
4297             Range<Float> lowLightBoostLuminanceRange =
4298                     c.get(CameraCharacteristics.CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE);
4299 
4300             // The AE mode low light boost can only be available if the luminance range is also
4301             // defined
4302             if (lowLightBoostLuminanceRange == null) {
4303                 assertFalse("AE mode ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY can only be present "
4304                         + "if LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE is also defined",
4305                         containsAeModeLowLightBoost);
4306                 continue;
4307             }
4308             assertTrue("AE mode ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY must be present if "
4309                     + "LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE is also defined",
4310                     containsAeModeLowLightBoost);
4311 
4312             float luminanceRangeLower = lowLightBoostLuminanceRange.getLower();
4313             assertTrue("Luminance range lower bound is in the range [0.1, 8.0]",
4314                     luminanceRangeLower >= 0.1f && luminanceRangeLower <= 8.0f);
4315         }
4316     }
4317 
4318     /**
4319      * Check key is present in characteristics if the hardware level is at least {@code hwLevel};
4320      * check that the key is present if the actual capabilities are one of {@code capabilities}.
4321      *
4322      * @return value of the {@code key} from {@code c}
4323      */
4324     private <T> T expectKeyAvailable(CameraCharacteristics c, CameraCharacteristics.Key<T> key,
4325             int hwLevel, int... capabilities) {
4326 
4327         Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
4328         assertNotNull("android.info.supportedHardwareLevel must never be null", actualHwLevel);
4329 
4330         int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
4331         assertNotNull("android.request.availableCapabilities must never be null",
4332                 actualCapabilities);
4333 
4334         List<Key<?>> allKeys = c.getKeys();
4335 
4336         T value = c.get(key);
4337 
4338         // For LIMITED-level targeted keys, rely on capability check, not level
4339         if ((compareHardwareLevel(actualHwLevel, hwLevel) >= 0) && (hwLevel != LIMITED)) {
4340             mCollector.expectTrue(
4341                     String.format("Key (%s) must be in characteristics for this hardware level " +
4342                             "(required minimal HW level %s, actual HW level %s)",
4343                             key.getName(), toStringHardwareLevel(hwLevel),
4344                             toStringHardwareLevel(actualHwLevel)),
4345                     value != null);
4346             mCollector.expectTrue(
4347                     String.format("Key (%s) must be in characteristics list of keys for this " +
4348                             "hardware level (required minimal HW level %s, actual HW level %s)",
4349                             key.getName(), toStringHardwareLevel(hwLevel),
4350                             toStringHardwareLevel(actualHwLevel)),
4351                     allKeys.contains(key));
4352         } else if (arrayContainsAnyOf(actualCapabilities, capabilities)) {
4353             if (!(hwLevel == LIMITED && compareHardwareLevel(actualHwLevel, hwLevel) < 0)) {
4354                 // Don't enforce LIMITED-starting keys on LEGACY level, even if cap is defined
4355                 mCollector.expectTrue(
4356                     String.format("Key (%s) must be in characteristics for these capabilities " +
4357                             "(required capabilities %s, actual capabilities %s)",
4358                             key.getName(), Arrays.toString(capabilities),
4359                             Arrays.toString(actualCapabilities)),
4360                     value != null);
4361                 mCollector.expectTrue(
4362                     String.format("Key (%s) must be in characteristics list of keys for " +
4363                             "these capabilities (required capabilities %s, actual capabilities %s)",
4364                             key.getName(), Arrays.toString(capabilities),
4365                             Arrays.toString(actualCapabilities)),
4366                     allKeys.contains(key));
4367             }
4368         } else {
4369             if (actualHwLevel == LEGACY && hwLevel != OPT) {
4370                 if (value != null || allKeys.contains(key)) {
4371                     Log.w(TAG, String.format(
4372                             "Key (%s) is not required for LEGACY devices but still appears",
4373                             key.getName()));
4374                 }
4375             }
4376             // OK: Key may or may not be present.
4377         }
4378         return value;
4379     }
4380 
4381     private static boolean arrayContains(int[] arr, int needle) {
4382         if (arr == null) {
4383             return false;
4384         }
4385 
4386         for (int elem : arr) {
4387             if (elem == needle) {
4388                 return true;
4389             }
4390         }
4391 
4392         return false;
4393     }
4394 
4395     private static <T> boolean arrayContains(T[] arr, T needle) {
4396         if (arr == null) {
4397             return false;
4398         }
4399 
4400         for (T elem : arr) {
4401             if (elem.equals(needle)) {
4402                 return true;
4403             }
4404         }
4405 
4406         return false;
4407     }
4408 
4409     private static boolean arrayContainsAnyOf(int[] arr, int[] needles) {
4410         for (int needle : needles) {
4411             if (arrayContains(arr, needle)) {
4412                 return true;
4413             }
4414         }
4415         return false;
4416     }
4417 
4418     /**
4419      * The key name has a prefix of either "android." or a valid TLD; other prefixes are not valid.
4420      */
4421     private static void assertKeyPrefixValid(String keyName) {
4422         assertStartsWithAndroidOrTLD(
4423                 "All metadata keys must start with 'android.' (built-in keys) " +
4424                 "or valid TLD (vendor-extended keys)", keyName);
4425     }
4426 
4427     private static void assertTrueForKey(String msg, CameraCharacteristics.Key<?> key,
4428             boolean actual) {
4429         assertTrue(msg + " (key = '" + key.getName() + "')", actual);
4430     }
4431 
4432     private static <T> void assertOneOf(String msg, T[] expected, T actual) {
4433         for (int i = 0; i < expected.length; ++i) {
4434             if (Objects.equals(expected[i], actual)) {
4435                 return;
4436             }
4437         }
4438 
4439         fail(String.format("%s: (expected one of %s, actual %s)",
4440                 msg, Arrays.toString(expected), actual));
4441     }
4442 
4443     private static <T> void assertStartsWithAndroidOrTLD(String msg, String keyName) {
4444         String delimiter = ".";
4445         if (keyName.startsWith(PREFIX_ANDROID + delimiter)) {
4446             return;
4447         }
4448         Pattern tldPattern = Pattern.compile(Patterns.TOP_LEVEL_DOMAIN_STR);
4449         Matcher match = tldPattern.matcher(keyName);
4450         if (match.find(0) && (0 == match.start()) && (!match.hitEnd())) {
4451             if (keyName.regionMatches(match.end(), delimiter, 0, delimiter.length())) {
4452                 return;
4453             }
4454         }
4455 
4456         fail(String.format("%s: (expected to start with %s or valid TLD, but value was %s)",
4457                 msg, PREFIX_ANDROID + delimiter, keyName));
4458     }
4459 
4460     /** Return a positive int if left > right, 0 if left==right, negative int if left < right */
4461     private static int compareHardwareLevel(int left, int right) {
4462         return remapHardwareLevel(left) - remapHardwareLevel(right);
4463     }
4464 
4465     /** Remap HW levels worst<->best, 0 = LEGACY, 1 = LIMITED, 2 = FULL, ..., N = LEVEL_N */
4466     private static int remapHardwareLevel(int level) {
4467         switch (level) {
4468             case OPT:
4469                 return Integer.MAX_VALUE;
4470             case LEGACY:
4471                 return 0; // lowest
4472             case EXTERNAL:
4473                 return 1; // second lowest
4474             case LIMITED:
4475                 return 2;
4476             case FULL:
4477                 return 3; // good
4478             case LEVEL_3:
4479                 return 4;
4480             default:
4481                 fail("Unknown HW level: " + level);
4482         }
4483         return -1;
4484     }
4485 
4486     private static String toStringHardwareLevel(int level) {
4487         switch (level) {
4488             case LEGACY:
4489                 return "LEGACY";
4490             case LIMITED:
4491                 return "LIMITED";
4492             case FULL:
4493                 return "FULL";
4494             case EXTERNAL:
4495                 return "EXTERNAL";
4496             default:
4497                 if (level >= LEVEL_3) {
4498                     return String.format("LEVEL_%d", level);
4499                 }
4500         }
4501 
4502         // unknown
4503         Log.w(TAG, "Unknown hardware level " + level);
4504         return Integer.toString(level);
4505     }
4506 }
4507