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