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