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