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 android.content.Context;
20 import android.graphics.ImageFormat;
21 import android.graphics.Rect;
22 import android.graphics.SurfaceTexture;
23 import android.hardware.Camera;
24 import android.hardware.camera2.CameraCharacteristics;
25 import android.hardware.camera2.CameraCharacteristics.Key;
26 import android.hardware.camera2.CameraDevice;
27 import android.hardware.camera2.CameraManager;
28 import android.hardware.camera2.CameraMetadata;
29 import android.hardware.camera2.CaptureRequest;
30 import android.hardware.camera2.CaptureResult;
31 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
32 import android.hardware.camera2.cts.helpers.StaticMetadata;
33 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
34 import android.hardware.camera2.params.BlackLevelPattern;
35 import android.hardware.camera2.params.ColorSpaceTransform;
36 import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
37 import android.hardware.camera2.params.StreamConfigurationMap;
38 import android.media.CamcorderProfile;
39 import android.media.ImageReader;
40 import android.os.Build;
41 import android.util.DisplayMetrics;
42 import android.util.Log;
43 import android.util.Rational;
44 import android.util.Range;
45 import android.util.Size;
46 import android.util.Pair;
47 import android.util.Patterns;
48 import android.view.Display;
49 import android.view.Surface;
50 import android.view.WindowManager;
51 
52 import com.android.compatibility.common.util.CddTest;
53 
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.List;
57 import java.util.Objects;
58 import java.util.regex.Matcher;
59 import java.util.regex.Pattern;
60 import java.util.Set;
61 
62 import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
63 import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
64 
65 import static org.mockito.Mockito.*;
66 
67 /**
68  * Extended tests for static camera characteristics.
69  */
70 public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase {
71     private static final String TAG = "ExChrsTest"; // must be short so next line doesn't throw
72     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
73 
74     private static final String PREFIX_ANDROID = "android";
75 
76     /*
77      * Constants for static RAW metadata.
78      */
79     private static final int MIN_ALLOWABLE_WHITELEVEL = 32; // must have sensor bit depth > 5
80 
81     private List<CameraCharacteristics> mCharacteristics;
82 
83     private static final Size FULLHD = new Size(1920, 1080);
84     private static final Size FULLHD_ALT = new Size(1920, 1088);
85     private static final Size HD = new Size(1280, 720);
86     private static final Size VGA = new Size(640, 480);
87     private static final Size QVGA = new Size(320, 240);
88 
89     private static final long MIN_BACK_SENSOR_RESOLUTION = 2000000;
90     private static final long MIN_FRONT_SENSOR_RESOLUTION = VGA.getHeight() * VGA.getWidth();
91     private static final long LOW_LATENCY_THRESHOLD_MS = 200;
92     private static final float LATENCY_TOLERANCE_FACTOR = 1.1f; // 10% tolerance
93     private static final float FOCAL_LENGTH_TOLERANCE = .01f;
94     private static final int MAX_NUM_IMAGES = 5;
95     private static final long PREVIEW_RUN_MS = 500;
96     private static final long FRAME_DURATION_30FPS_NSEC = (long) 1e9 / 30;
97     /*
98      * HW Levels short hand
99      */
100     private static final int LEGACY = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
101     private static final int LIMITED = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
102     private static final int FULL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
103     private static final int LEVEL_3 = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3;
104     private static final int EXTERNAL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
105     private static final int OPT = Integer.MAX_VALUE;  // For keys that are optional on all hardware levels.
106 
107     /*
108      * Capabilities short hand
109      */
110     private static final int NONE = -1;
111     private static final int BC =
112             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
113     private static final int MANUAL_SENSOR =
114             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR;
115     private static final int MANUAL_POSTPROC =
116             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING;
117     private static final int RAW =
118             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW;
119     private static final int YUV_REPROCESS =
120             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING;
121     private static final int OPAQUE_REPROCESS =
122             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
123     private static final int CONSTRAINED_HIGH_SPEED =
124             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO;
125     private static final int MONOCHROME =
126             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME;
127     private static final int HIGH_SPEED_FPS_LOWER_MIN = 30;
128     private static final int HIGH_SPEED_FPS_UPPER_MIN = 120;
129 
130     @Override
setUp()131     protected void setUp() throws Exception {
132         super.setUp();
133         mCharacteristics = new ArrayList<>();
134         for (int i = 0; i < mAllCameraIds.length; i++) {
135             mCharacteristics.add(mAllStaticInfo.get(mAllCameraIds[i]).getCharacteristics());
136         }
137     }
138 
139     @Override
tearDown()140     protected void tearDown() throws Exception {
141         super.tearDown();
142         mCharacteristics = null;
143     }
144 
145     /**
146      * Test that the available stream configurations contain a few required formats and sizes.
147      */
148     @CddTest(requirement="7.5.1/C-1-2")
testAvailableStreamConfigs()149     public void testAvailableStreamConfigs() throws Exception {
150         boolean firstBackFacingCamera = true;
151         for (int i = 0; i < mAllCameraIds.length; i++) {
152             CameraCharacteristics c = mCharacteristics.get(i);
153             StreamConfigurationMap config =
154                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
155             assertNotNull(String.format("No stream configuration map found for: ID %s",
156                     mAllCameraIds[i]), config);
157             int[] outputFormats = config.getOutputFormats();
158 
159             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
160             assertNotNull("android.request.availableCapabilities must never be null",
161                     actualCapabilities);
162 
163             // Check required formats exist (JPEG, and YUV_420_888).
164             if (!arrayContains(actualCapabilities, BC)) {
165                 Log.i(TAG, "Camera " + mAllCameraIds[i] +
166                     ": BACKWARD_COMPATIBLE capability not supported, skipping test");
167                 continue;
168             }
169 
170             boolean isMonochromeWithY8 = arrayContains(actualCapabilities, MONOCHROME)
171                     && arrayContains(outputFormats, ImageFormat.Y8);
172             boolean isHiddenPhysicalCamera = !arrayContains(mCameraIds, mAllCameraIds[i]);
173             boolean supportHeic = arrayContains(outputFormats, ImageFormat.HEIC);
174 
175             assertArrayContains(
176                     String.format("No valid YUV_420_888 preview formats found for: ID %s",
177                             mAllCameraIds[i]), outputFormats, ImageFormat.YUV_420_888);
178             if (isMonochromeWithY8) {
179                 assertArrayContains(
180                         String.format("No valid Y8 preview formats found for: ID %s",
181                                 mAllCameraIds[i]), outputFormats, ImageFormat.Y8);
182             }
183             assertArrayContains(String.format("No JPEG image format for: ID %s",
184                     mAllCameraIds[i]), outputFormats, ImageFormat.JPEG);
185 
186             Size[] yuvSizes = config.getOutputSizes(ImageFormat.YUV_420_888);
187             Size[] y8Sizes = config.getOutputSizes(ImageFormat.Y8);
188             Size[] jpegSizes = config.getOutputSizes(ImageFormat.JPEG);
189             Size[] heicSizes = config.getOutputSizes(ImageFormat.HEIC);
190             Size[] privateSizes = config.getOutputSizes(ImageFormat.PRIVATE);
191 
192             CameraTestUtils.assertArrayNotEmpty(yuvSizes,
193                     String.format("No sizes for preview format %x for: ID %s",
194                             ImageFormat.YUV_420_888, mAllCameraIds[i]));
195             if (isMonochromeWithY8) {
196                 CameraTestUtils.assertArrayNotEmpty(y8Sizes,
197                     String.format("No sizes for preview format %x for: ID %s",
198                             ImageFormat.Y8, mAllCameraIds[i]));
199             }
200 
201             Rect activeRect = CameraTestUtils.getValueNotNull(
202                     c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
203             Size pixelArraySize = CameraTestUtils.getValueNotNull(
204                     c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
205 
206             int activeArrayHeight = activeRect.height();
207             int activeArrayWidth = activeRect.width();
208             long sensorResolution = pixelArraySize.getHeight() * pixelArraySize.getWidth() ;
209             Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
210             assertNotNull("Can't get lens facing info for camera id: " + mAllCameraIds[i],
211                     lensFacing);
212 
213             // Check that the sensor sizes are atleast what the CDD specifies
214             switch(lensFacing) {
215                 case CameraCharacteristics.LENS_FACING_FRONT:
216                     assertTrue("Front Sensor resolution should be at least " +
217                             MIN_FRONT_SENSOR_RESOLUTION + " pixels, is "+ sensorResolution,
218                             sensorResolution >= MIN_FRONT_SENSOR_RESOLUTION);
219                     break;
220                 case CameraCharacteristics.LENS_FACING_BACK:
221                     if (firstBackFacingCamera) {
222                         assertTrue("Back Sensor resolution should be at least "
223                                 + MIN_BACK_SENSOR_RESOLUTION +
224                                 " pixels, is "+ sensorResolution,
225                                 sensorResolution >= MIN_BACK_SENSOR_RESOLUTION);
226                         firstBackFacingCamera = false;
227                     }
228                     break;
229                 default:
230                     break;
231             }
232 
233             Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
234 
235             if (activeArrayWidth >= FULLHD.getWidth() &&
236                     activeArrayHeight >= FULLHD.getHeight()) {
237                 assertArrayContainsAnyOf(String.format(
238                         "Required FULLHD size not found for format %x for: ID %s",
239                         ImageFormat.JPEG, mAllCameraIds[i]), jpegSizes,
240                         new Size[] {FULLHD, FULLHD_ALT});
241                 if (supportHeic) {
242                     assertArrayContainsAnyOf(String.format(
243                             "Required FULLHD size not found for format %x for: ID %s",
244                             ImageFormat.HEIC, mAllCameraIds[i]), heicSizes,
245                             new Size[] {FULLHD, FULLHD_ALT});
246                 }
247             }
248 
249             if (activeArrayWidth >= HD.getWidth() &&
250                     activeArrayHeight >= HD.getHeight()) {
251                 assertArrayContains(String.format(
252                         "Required HD size not found for format %x for: ID %s",
253                         ImageFormat.JPEG, mAllCameraIds[i]), jpegSizes, HD);
254                 if (supportHeic) {
255                     assertArrayContains(String.format(
256                             "Required HD size not found for format %x for: ID %s",
257                             ImageFormat.HEIC, mAllCameraIds[i]), heicSizes, HD);
258                 }
259             }
260 
261             if (activeArrayWidth >= VGA.getWidth() &&
262                     activeArrayHeight >= VGA.getHeight()) {
263                 assertArrayContains(String.format(
264                         "Required VGA size not found for format %x for: ID %s",
265                         ImageFormat.JPEG, mAllCameraIds[i]), jpegSizes, VGA);
266                 if (supportHeic) {
267                     assertArrayContains(String.format(
268                             "Required VGA size not found for format %x for: ID %s",
269                             ImageFormat.HEIC, mAllCameraIds[i]), heicSizes, VGA);
270                 }
271             }
272 
273             if (activeArrayWidth >= QVGA.getWidth() &&
274                     activeArrayHeight >= QVGA.getHeight()) {
275                 assertArrayContains(String.format(
276                         "Required QVGA size not found for format %x for: ID %s",
277                         ImageFormat.JPEG, mAllCameraIds[i]), jpegSizes, QVGA);
278                 if (supportHeic) {
279                     assertArrayContains(String.format(
280                             "Required QVGA size not found for format %x for: ID %s",
281                             ImageFormat.HEIC, mAllCameraIds[i]), heicSizes, QVGA);
282                 }
283 
284             }
285 
286             ArrayList<Size> jpegSizesList = new ArrayList<>(Arrays.asList(jpegSizes));
287             ArrayList<Size> yuvSizesList = new ArrayList<>(Arrays.asList(yuvSizes));
288             ArrayList<Size> privateSizesList = new ArrayList<>(Arrays.asList(privateSizes));
289             boolean isExternalCamera = (hwLevel ==
290                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
291             Size maxVideoSize = null;
292             if (isExternalCamera || isHiddenPhysicalCamera) {
293                 // TODO: for now, use FULLHD 30 as largest possible video size for external camera.
294                 // For hidden physical camera, since we don't require CamcorderProfile to be
295                 // available, use FULLHD 30 as maximum video size as well.
296                 List<Size> videoSizes = CameraTestUtils.getSupportedVideoSizes(
297                         mAllCameraIds[i], mCameraManager, FULLHD);
298                 for (Size sz : videoSizes) {
299                     long minFrameDuration = config.getOutputMinFrameDuration(
300                             android.media.MediaRecorder.class, sz);
301                     // Give some margin for rounding error
302                     if (minFrameDuration < (1e9 / 29.9)) {
303                         maxVideoSize = sz;
304                         break;
305                     }
306                 }
307             } else {
308                 int cameraId = Integer.valueOf(mAllCameraIds[i]);
309                 CamcorderProfile maxVideoProfile = CamcorderProfile.get(
310                         cameraId, CamcorderProfile.QUALITY_HIGH);
311                 maxVideoSize = new Size(
312                         maxVideoProfile.videoFrameWidth, maxVideoProfile.videoFrameHeight);
313             }
314             if (maxVideoSize == null) {
315                 fail("Camera " + mAllCameraIds[i] + " does not support any 30fps video output");
316             }
317 
318             // Handle FullHD special case first
319             if (jpegSizesList.contains(FULLHD)) {
320                 if (compareHardwareLevel(hwLevel, LEVEL_3) >= 0 || hwLevel == FULL ||
321                         (hwLevel == LIMITED &&
322                         maxVideoSize.getWidth() >= FULLHD.getWidth() &&
323                         maxVideoSize.getHeight() >= FULLHD.getHeight())) {
324                     boolean yuvSupportFullHD = yuvSizesList.contains(FULLHD) ||
325                             yuvSizesList.contains(FULLHD_ALT);
326                     boolean privateSupportFullHD = privateSizesList.contains(FULLHD) ||
327                             privateSizesList.contains(FULLHD_ALT);
328                     assertTrue("Full device FullHD YUV size not found", yuvSupportFullHD);
329                     assertTrue("Full device FullHD PRIVATE size not found", privateSupportFullHD);
330 
331                     if (isMonochromeWithY8) {
332                         ArrayList<Size> y8SizesList = new ArrayList<>(Arrays.asList(y8Sizes));
333                         boolean y8SupportFullHD = y8SizesList.contains(FULLHD) ||
334                                 y8SizesList.contains(FULLHD_ALT);
335                         assertTrue("Full device FullHD Y8 size not found", y8SupportFullHD);
336                     }
337                 }
338                 // remove all FullHD or FullHD_Alt sizes for the remaining of the test
339                 jpegSizesList.remove(FULLHD);
340                 jpegSizesList.remove(FULLHD_ALT);
341             }
342 
343             // Check all sizes other than FullHD
344             if (hwLevel == LIMITED) {
345                 // Remove all jpeg sizes larger than max video size
346                 ArrayList<Size> toBeRemoved = new ArrayList<>();
347                 for (Size size : jpegSizesList) {
348                     if (size.getWidth() >= maxVideoSize.getWidth() &&
349                             size.getHeight() >= maxVideoSize.getHeight()) {
350                         toBeRemoved.add(size);
351                     }
352                 }
353                 jpegSizesList.removeAll(toBeRemoved);
354             }
355 
356             if (compareHardwareLevel(hwLevel, LEVEL_3) >= 0 || hwLevel == FULL ||
357                     hwLevel == LIMITED) {
358                 if (!yuvSizesList.containsAll(jpegSizesList)) {
359                     for (Size s : jpegSizesList) {
360                         if (!yuvSizesList.contains(s)) {
361                             fail("Size " + s + " not found in YUV format");
362                         }
363                     }
364                 }
365 
366                 if (isMonochromeWithY8) {
367                     ArrayList<Size> y8SizesList = new ArrayList<>(Arrays.asList(y8Sizes));
368                     if (!y8SizesList.containsAll(jpegSizesList)) {
369                         for (Size s : jpegSizesList) {
370                             if (!y8SizesList.contains(s)) {
371                                 fail("Size " + s + " not found in Y8 format");
372                             }
373                         }
374                     }
375                 }
376             }
377 
378             if (!privateSizesList.containsAll(yuvSizesList)) {
379                 for (Size s : yuvSizesList) {
380                     if (!privateSizesList.contains(s)) {
381                         fail("Size " + s + " not found in PRIVATE format");
382                     }
383                 }
384             }
385         }
386     }
387 
verifyCommonRecommendedConfiguration(String id, CameraCharacteristics c, RecommendedStreamConfigurationMap config, boolean checkNoInput, boolean checkNoHighRes, boolean checkNoHighSpeed, boolean checkNoPrivate, boolean checkNoDepth)388     private void verifyCommonRecommendedConfiguration(String id, CameraCharacteristics c,
389             RecommendedStreamConfigurationMap config, boolean checkNoInput,
390             boolean checkNoHighRes, boolean checkNoHighSpeed, boolean checkNoPrivate,
391             boolean checkNoDepth) {
392         StreamConfigurationMap fullConfig = c.get(
393                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
394         assertNotNull(String.format("No stream configuration map found for ID: %s!", id),
395                 fullConfig);
396 
397         Set<Integer> recommendedOutputFormats = config.getOutputFormats();
398 
399         if (checkNoInput) {
400             Set<Integer> inputFormats = config.getInputFormats();
401             assertTrue(String.format("Recommended configuration must not include any input " +
402                     "streams for ID: %s", id),
403                     ((inputFormats == null) || (inputFormats.size() == 0)));
404         }
405 
406         if (checkNoHighRes) {
407             for (int format : recommendedOutputFormats) {
408                 Set<Size> highResSizes = config.getHighResolutionOutputSizes(format);
409                 assertTrue(String.format("Recommended configuration should not include any " +
410                         "high resolution sizes, which cannot operate at full " +
411                         "BURST_CAPTURE rate for ID: %s", id),
412                         ((highResSizes == null) || (highResSizes.size() == 0)));
413             }
414         }
415 
416         if (checkNoHighSpeed) {
417             Set<Size> highSpeedSizes = config.getHighSpeedVideoSizes();
418             assertTrue(String.format("Recommended configuration must not include any high " +
419                     "speed configurations for ID: %s", id),
420                     ((highSpeedSizes == null) || (highSpeedSizes.size() == 0)));
421         }
422 
423         int[] exhaustiveOutputFormats = fullConfig.getOutputFormats();
424         for (Integer formatInteger : recommendedOutputFormats) {
425             int format = formatInteger.intValue();
426             assertArrayContains(String.format("Unsupported recommended output format: %d for " +
427                     "ID: %s ", format, id), exhaustiveOutputFormats, format);
428             Set<Size> recommendedSizes = config.getOutputSizes(format);
429 
430             switch (format) {
431                 case ImageFormat.PRIVATE:
432                     if (checkNoPrivate) {
433                         fail(String.format("Recommended configuration must not include " +
434                                 "PRIVATE format entries for ID: %s", id));
435                     }
436 
437                     Set<Size> classOutputSizes = config.getOutputSizes(ImageReader.class);
438                     assertCollectionContainsAnyOf(String.format("Recommended output sizes for " +
439                             "ImageReader class don't match the output sizes for the " +
440                             "corresponding format for ID: %s", id), classOutputSizes,
441                             recommendedSizes);
442                     break;
443                 case ImageFormat.DEPTH16:
444                 case ImageFormat.DEPTH_POINT_CLOUD:
445                     if (checkNoDepth) {
446                         fail(String.format("Recommended configuration must not include any DEPTH " +
447                                 "formats for ID: %s", id));
448                     }
449                     break;
450                 default:
451             }
452             Size [] exhaustiveSizes = fullConfig.getOutputSizes(format);
453             for (Size sz : recommendedSizes) {
454                 assertArrayContains(String.format("Unsupported recommended size %s for " +
455                         "format: %d for ID: %s", sz.toString(), format, id),
456                         exhaustiveSizes, sz);
457 
458                 long recommendedMinDuration = config.getOutputMinFrameDuration(format, sz);
459                 long availableMinDuration = fullConfig.getOutputMinFrameDuration(format, sz);
460                 assertTrue(String.format("Recommended minimum frame duration %d for size " +
461                         "%s format: %d doesn't match with currently available minimum" +
462                         " frame duration of %d for ID: %s", recommendedMinDuration,
463                         sz.toString(), format, availableMinDuration, id),
464                         (recommendedMinDuration == availableMinDuration));
465                 long recommendedStallDuration = config.getOutputStallDuration(format, sz);
466                 long availableStallDuration = fullConfig.getOutputStallDuration(format, sz);
467                 assertTrue(String.format("Recommended stall duration %d for size %s" +
468                         " format: %d doesn't match with currently available stall " +
469                         "duration of %d for ID: %s", recommendedStallDuration,
470                         sz.toString(), format, availableStallDuration, id),
471                         (recommendedStallDuration == availableStallDuration));
472 
473                 ImageReader reader = ImageReader.newInstance(sz.getWidth(), sz.getHeight(), format,
474                         /*maxImages*/1);
475                 Surface readerSurface = reader.getSurface();
476                 assertTrue(String.format("ImageReader surface using format %d and size %s is not" +
477                         " supported for ID: %s", format, sz.toString(), id),
478                         config.isOutputSupportedFor(readerSurface));
479                 if (format == ImageFormat.PRIVATE) {
480                     long classMinDuration = config.getOutputMinFrameDuration(ImageReader.class, sz);
481                     assertTrue(String.format("Recommended minimum frame duration %d for size " +
482                             "%s format: %d doesn't match with the duration %d for " +
483                             "ImageReader class of the same size", recommendedMinDuration,
484                             sz.toString(), format, classMinDuration),
485                             classMinDuration == recommendedMinDuration);
486                     long classStallDuration = config.getOutputStallDuration(ImageReader.class, sz);
487                     assertTrue(String.format("Recommended stall duration %d for size " +
488                             "%s format: %d doesn't match with the stall duration %d for " +
489                             "ImageReader class of the same size", recommendedStallDuration,
490                             sz.toString(), format, classStallDuration),
491                             classStallDuration == recommendedStallDuration);
492                 }
493             }
494         }
495     }
496 
verifyRecommendedPreviewConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap previewConfig)497     private void verifyRecommendedPreviewConfiguration(String cameraId, CameraCharacteristics c,
498             RecommendedStreamConfigurationMap previewConfig) {
499         verifyCommonRecommendedConfiguration(cameraId, c, previewConfig, /*checkNoInput*/ true,
500                 /*checkNoHighRes*/ true, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/ false,
501                 /*checkNoDepth*/ true);
502 
503         Set<Integer> outputFormats = previewConfig.getOutputFormats();
504         assertTrue(String.format("No valid YUV_420_888 and PRIVATE preview " +
505                 "formats found in recommended preview configuration for ID: %s", cameraId),
506                 outputFormats.containsAll(Arrays.asList(new Integer(ImageFormat.YUV_420_888),
507                         new Integer(ImageFormat.PRIVATE))));
508     }
509 
verifyRecommendedVideoConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap videoConfig)510     private void verifyRecommendedVideoConfiguration(String cameraId, CameraCharacteristics c,
511             RecommendedStreamConfigurationMap videoConfig) {
512         verifyCommonRecommendedConfiguration(cameraId, c, videoConfig, /*checkNoInput*/ true,
513                 /*checkNoHighRes*/ true, /*checkNoHighSpeed*/ false, /*checkNoPrivate*/false,
514                 /*checkNoDepth*/ true);
515 
516         Set<Size> highSpeedSizes = videoConfig.getHighSpeedVideoSizes();
517         StreamConfigurationMap fullConfig = c.get(
518                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
519         assertNotNull("No stream configuration map found!", fullConfig);
520         Size [] availableHighSpeedSizes = fullConfig.getHighSpeedVideoSizes();
521         if ((highSpeedSizes != null) && (highSpeedSizes.size() > 0)) {
522             for (Size sz : highSpeedSizes) {
523                 assertArrayContains(String.format("Recommended video configuration includes " +
524                         "unsupported high speed configuration with size %s for ID: %s",
525                         sz.toString(), cameraId), availableHighSpeedSizes, sz);
526                 Set<Range<Integer>>  highSpeedFpsRanges =
527                     videoConfig.getHighSpeedVideoFpsRangesFor(sz);
528                 Range<Integer> [] availableHighSpeedFpsRanges =
529                     fullConfig.getHighSpeedVideoFpsRangesFor(sz);
530                 for (Range<Integer> fpsRange : highSpeedFpsRanges) {
531                     assertArrayContains(String.format("Recommended video configuration includes " +
532                             "unsupported high speed fps range [%d %d] for ID: %s",
533                             fpsRange.getLower().intValue(), fpsRange.getUpper().intValue(),
534                             cameraId), availableHighSpeedFpsRanges, fpsRange);
535                 }
536             }
537         }
538 
539         final int[] profileList = {
540             CamcorderProfile.QUALITY_2160P,
541             CamcorderProfile.QUALITY_1080P,
542             CamcorderProfile.QUALITY_480P,
543             CamcorderProfile.QUALITY_720P,
544             CamcorderProfile.QUALITY_CIF,
545             CamcorderProfile.QUALITY_HIGH,
546             CamcorderProfile.QUALITY_LOW,
547             CamcorderProfile.QUALITY_QCIF,
548             CamcorderProfile.QUALITY_QVGA,
549         };
550         Set<Size> privateSizeSet = videoConfig.getOutputSizes(ImageFormat.PRIVATE);
551         for (int profile : profileList) {
552             int idx = Integer.valueOf(cameraId);
553             if (CamcorderProfile.hasProfile(idx, profile)) {
554                 CamcorderProfile videoProfile = CamcorderProfile.get(idx, profile);
555                 Size profileSize  = new Size(videoProfile.videoFrameWidth,
556                         videoProfile.videoFrameHeight);
557                 assertCollectionContainsAnyOf(String.format("Recommended video configuration " +
558                         "doesn't include supported video profile size %s with Private format " +
559                         "for ID: %s", profileSize.toString(), cameraId), privateSizeSet,
560                         Arrays.asList(profileSize));
561             }
562         }
563     }
564 
isSizeWithinSensorMargin(Size sz, Size sensorSize)565     private Pair<Boolean, Size> isSizeWithinSensorMargin(Size sz, Size sensorSize) {
566         final float SIZE_ERROR_MARGIN = 0.03f;
567         float croppedWidth = (float)sensorSize.getWidth();
568         float croppedHeight = (float)sensorSize.getHeight();
569         float sensorAspectRatio = (float)sensorSize.getWidth() / (float)sensorSize.getHeight();
570         float maxAspectRatio = (float)sz.getWidth() / (float)sz.getHeight();
571         if (sensorAspectRatio < maxAspectRatio) {
572             croppedHeight = (float)sensorSize.getWidth() / maxAspectRatio;
573         } else if (sensorAspectRatio > maxAspectRatio) {
574             croppedWidth = (float)sensorSize.getHeight() * maxAspectRatio;
575         }
576         Size croppedSensorSize = new Size((int)croppedWidth, (int)croppedHeight);
577 
578         Boolean match = new Boolean(
579             (sz.getWidth() <= croppedSensorSize.getWidth() * (1.0 + SIZE_ERROR_MARGIN) &&
580              sz.getWidth() >= croppedSensorSize.getWidth() * (1.0 - SIZE_ERROR_MARGIN) &&
581              sz.getHeight() <= croppedSensorSize.getHeight() * (1.0 + SIZE_ERROR_MARGIN) &&
582              sz.getHeight() >= croppedSensorSize.getHeight() * (1.0 - SIZE_ERROR_MARGIN)));
583 
584         return Pair.create(match, croppedSensorSize);
585     }
586 
verifyRecommendedSnapshotConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap snapshotConfig)587     private void verifyRecommendedSnapshotConfiguration(String cameraId, CameraCharacteristics c,
588             RecommendedStreamConfigurationMap snapshotConfig) {
589         verifyCommonRecommendedConfiguration(cameraId, c, snapshotConfig, /*checkNoInput*/ true,
590                 /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/false,
591                 /*checkNoDepth*/ false);
592         Rect activeRect = CameraTestUtils.getValueNotNull(
593                 c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
594         Size arraySize = new Size(activeRect.width(), activeRect.height());
595 
596         Set<Size> snapshotSizeSet = snapshotConfig.getOutputSizes(ImageFormat.JPEG);
597         Size[] snapshotSizes = new Size[snapshotSizeSet.size()];
598         snapshotSizes = snapshotSizeSet.toArray(snapshotSizes);
599         Size maxJpegSize = CameraTestUtils.getMaxSize(snapshotSizes);
600         assertTrue(String.format("Maximum recommended Jpeg size %s should be within 3 percent " +
601                 "of the area of the advertised array size %s for ID: %s",
602                 maxJpegSize.toString(), arraySize.toString(), cameraId),
603                 isSizeWithinSensorMargin(maxJpegSize, arraySize).first.booleanValue());
604     }
605 
verifyRecommendedVideoSnapshotConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap videoSnapshotConfig, RecommendedStreamConfigurationMap videoConfig)606     private void verifyRecommendedVideoSnapshotConfiguration(String cameraId,
607             CameraCharacteristics c,
608             RecommendedStreamConfigurationMap videoSnapshotConfig,
609             RecommendedStreamConfigurationMap videoConfig) {
610         verifyCommonRecommendedConfiguration(cameraId, c, videoSnapshotConfig,
611                 /*checkNoInput*/ true, /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true,
612                 /*checkNoPrivate*/ true, /*checkNoDepth*/ true);
613 
614         Set<Integer> outputFormats = videoSnapshotConfig.getOutputFormats();
615         assertCollectionContainsAnyOf(String.format("No valid JPEG format found " +
616                 "in recommended video snapshot configuration for ID: %s", cameraId),
617                 outputFormats, Arrays.asList(new Integer(ImageFormat.JPEG)));
618         assertTrue(String.format("Recommended video snapshot configuration must only advertise " +
619                 "JPEG format for ID: %s", cameraId), outputFormats.size() == 1);
620 
621         Set<Size> privateVideoSizeSet = videoConfig.getOutputSizes(ImageFormat.PRIVATE);
622         Size[] privateVideoSizes = new Size[privateVideoSizeSet.size()];
623         privateVideoSizes = privateVideoSizeSet.toArray(privateVideoSizes);
624         Size maxVideoSize = CameraTestUtils.getMaxSize(privateVideoSizes);
625         Set<Size> outputSizes = videoSnapshotConfig.getOutputSizes(ImageFormat.JPEG);
626         assertCollectionContainsAnyOf(String.format("The maximum recommended video size %s " +
627                 "should be present in the recommended video snapshot configurations for ID: %s",
628                 maxVideoSize.toString(), cameraId), outputSizes, Arrays.asList(maxVideoSize));
629     }
630 
verifyRecommendedRawConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap rawConfig)631     private void verifyRecommendedRawConfiguration(String cameraId,
632             CameraCharacteristics c, RecommendedStreamConfigurationMap rawConfig) {
633         verifyCommonRecommendedConfiguration(cameraId, c, rawConfig, /*checkNoInput*/ true,
634                 /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/ true,
635                 /*checkNoDepth*/ true);
636 
637         Set<Integer> outputFormats = rawConfig.getOutputFormats();
638         for (Integer outputFormatInteger : outputFormats) {
639             int outputFormat = outputFormatInteger.intValue();
640             switch (outputFormat) {
641                 case ImageFormat.RAW10:
642                 case ImageFormat.RAW12:
643                 case ImageFormat.RAW_PRIVATE:
644                 case ImageFormat.RAW_SENSOR:
645                     break;
646                 default:
647                     fail(String.format("Recommended raw configuration map must not contain " +
648                             " non-RAW formats like: %d for ID: %s", outputFormat, cameraId));
649 
650             }
651         }
652     }
653 
verifyRecommendedZSLConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap zslConfig)654     private void verifyRecommendedZSLConfiguration(String cameraId, CameraCharacteristics c,
655             RecommendedStreamConfigurationMap zslConfig) {
656         verifyCommonRecommendedConfiguration(cameraId, c, zslConfig, /*checkNoInput*/ false,
657                 /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/ false,
658                 /*checkNoDepth*/ false);
659 
660         StreamConfigurationMap fullConfig =
661             c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
662         assertNotNull(String.format("No stream configuration map found for ID: %s!", cameraId),
663                 fullConfig);
664         Set<Integer> inputFormats = zslConfig.getInputFormats();
665         int [] availableInputFormats = fullConfig.getInputFormats();
666         for (Integer inputFormatInteger : inputFormats) {
667             int inputFormat = inputFormatInteger.intValue();
668             assertArrayContains(String.format("Recommended ZSL configuration includes " +
669                     "unsupported input format %d for ID: %s", inputFormat, cameraId),
670                     availableInputFormats, inputFormat);
671 
672             Set<Size> inputSizes = zslConfig.getInputSizes(inputFormat);
673             Size [] availableInputSizes = fullConfig.getInputSizes(inputFormat);
674             assertTrue(String.format("Recommended ZSL configuration input format %d includes " +
675                     "invalid input sizes for ID: %s", inputFormat, cameraId),
676                     ((inputSizes != null) && (inputSizes.size() > 0)));
677             for (Size inputSize : inputSizes) {
678                 assertArrayContains(String.format("Recommended ZSL configuration includes " +
679                         "unsupported input format %d with size %s ID: %s", inputFormat,
680                         inputSize.toString(), cameraId), availableInputSizes, inputSize);
681             }
682             Set<Integer> validOutputFormats = zslConfig.getValidOutputFormatsForInput(inputFormat);
683             int [] availableValidOutputFormats = fullConfig.getValidOutputFormatsForInput(
684                     inputFormat);
685             for (Integer outputFormatInteger : validOutputFormats) {
686                 int outputFormat = outputFormatInteger.intValue();
687                 assertArrayContains(String.format("Recommended ZSL configuration includes " +
688                         "unsupported output format %d for input %s ID: %s", outputFormat,
689                         inputFormat, cameraId), availableValidOutputFormats, outputFormat);
690             }
691         }
692     }
693 
checkFormatLatency(int format, long latencyThresholdMs, RecommendedStreamConfigurationMap configMap)694     private void checkFormatLatency(int format, long latencyThresholdMs,
695             RecommendedStreamConfigurationMap configMap) throws Exception {
696         Set<Size> availableSizes = configMap.getOutputSizes(format);
697         assertNotNull(String.format("No available sizes for output format: %d", format),
698                 availableSizes);
699 
700         ImageReader previewReader = null;
701         long threshold = (long) (latencyThresholdMs * LATENCY_TOLERANCE_FACTOR);
702         // for each resolution, check that the end-to-end latency doesn't exceed the given threshold
703         for (Size sz : availableSizes) {
704             try {
705                 // Create ImageReaders, capture session and requests
706                 final ImageReader.OnImageAvailableListener mockListener = mock(
707                         ImageReader.OnImageAvailableListener.class);
708                 createDefaultImageReader(sz, format, MAX_NUM_IMAGES, mockListener);
709                 Size previewSize = mOrderedPreviewSizes.get(0);
710                 previewReader = createImageReader(previewSize, ImageFormat.YUV_420_888,
711                         MAX_NUM_IMAGES, new CameraTestUtils.ImageDropperListener());
712                 Surface previewSurface = previewReader.getSurface();
713                 List<Surface> surfaces = new ArrayList<Surface>();
714                 surfaces.add(previewSurface);
715                 surfaces.add(mReaderSurface);
716                 createSession(surfaces);
717                 CaptureRequest.Builder captureBuilder =
718                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
719                 captureBuilder.addTarget(previewSurface);
720                 CaptureRequest request = captureBuilder.build();
721 
722                 // Let preview run for a while
723                 startCapture(request, /*repeating*/ true, new SimpleCaptureCallback(), mHandler);
724                 Thread.sleep(PREVIEW_RUN_MS);
725 
726                 // Start capture.
727                 captureBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
728                 captureBuilder.addTarget(mReaderSurface);
729                 request = captureBuilder.build();
730 
731                 for (int i = 0; i < MAX_NUM_IMAGES; i++) {
732                     startCapture(request, /*repeating*/ false, new SimpleCaptureCallback(),
733                             mHandler);
734                     verify(mockListener, timeout(threshold).times(1)).onImageAvailable(
735                             any(ImageReader.class));
736                     reset(mockListener);
737                 }
738 
739                 // stop capture.
740                 stopCapture(/*fast*/ false);
741             } finally {
742                 closeDefaultImageReader();
743 
744                 if (previewReader != null) {
745                     previewReader.close();
746                     previewReader = null;
747                 }
748             }
749 
750         }
751     }
752 
verifyRecommendedLowLatencyConfiguration(String cameraId, CameraCharacteristics c, RecommendedStreamConfigurationMap lowLatencyConfig)753     private void verifyRecommendedLowLatencyConfiguration(String cameraId, CameraCharacteristics c,
754             RecommendedStreamConfigurationMap lowLatencyConfig) throws Exception {
755         verifyCommonRecommendedConfiguration(cameraId, c, lowLatencyConfig, /*checkNoInput*/ true,
756                 /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/ false,
757                 /*checkNoDepth*/ true);
758 
759         try {
760             openDevice(cameraId);
761 
762             Set<Integer> formats = lowLatencyConfig.getOutputFormats();
763             for (Integer format : formats) {
764                 checkFormatLatency(format.intValue(), LOW_LATENCY_THRESHOLD_MS, lowLatencyConfig);
765             }
766         } finally {
767             closeDevice(cameraId);
768         }
769 
770     }
771 
testRecommendedStreamConfigurations()772     public void testRecommendedStreamConfigurations() throws Exception {
773         for (int i = 0; i < mAllCameraIds.length; i++) {
774             CameraCharacteristics c = mCharacteristics.get(i);
775             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
776             assertNotNull("android.request.availableCapabilities must never be null",
777                     actualCapabilities);
778 
779             if (!arrayContains(actualCapabilities, BC)) {
780                 Log.i(TAG, "Camera " + mAllCameraIds[i] +
781                         ": BACKWARD_COMPATIBLE capability not supported, skipping test");
782                 continue;
783             }
784 
785             try {
786                 RecommendedStreamConfigurationMap map = c.getRecommendedStreamConfigurationMap(
787                         RecommendedStreamConfigurationMap.USECASE_PREVIEW - 1);
788                 fail("Recommended configuration map shouldn't be available for invalid " +
789                         "use case!");
790             } catch (IllegalArgumentException e) {
791                 //Expected continue
792             }
793 
794             try {
795                 RecommendedStreamConfigurationMap map = c.getRecommendedStreamConfigurationMap(
796                         RecommendedStreamConfigurationMap.USECASE_LOW_LATENCY_SNAPSHOT + 1);
797                 fail("Recommended configuration map shouldn't be available for invalid " +
798                         "use case!");
799             } catch (IllegalArgumentException e) {
800                 //Expected continue
801             }
802 
803             RecommendedStreamConfigurationMap previewConfig =
804                     c.getRecommendedStreamConfigurationMap(
805                     RecommendedStreamConfigurationMap.USECASE_PREVIEW);
806             RecommendedStreamConfigurationMap videoRecordingConfig =
807                     c.getRecommendedStreamConfigurationMap(
808                     RecommendedStreamConfigurationMap.USECASE_RECORD);
809             RecommendedStreamConfigurationMap videoSnapshotConfig =
810                     c.getRecommendedStreamConfigurationMap(
811                     RecommendedStreamConfigurationMap.USECASE_VIDEO_SNAPSHOT);
812             RecommendedStreamConfigurationMap snapshotConfig =
813                     c.getRecommendedStreamConfigurationMap(
814                     RecommendedStreamConfigurationMap.USECASE_SNAPSHOT);
815             RecommendedStreamConfigurationMap rawConfig =
816                     c.getRecommendedStreamConfigurationMap(
817                     RecommendedStreamConfigurationMap.USECASE_RAW);
818             RecommendedStreamConfigurationMap zslConfig =
819                     c.getRecommendedStreamConfigurationMap(
820                     RecommendedStreamConfigurationMap.USECASE_ZSL);
821             RecommendedStreamConfigurationMap lowLatencyConfig =
822                     c.getRecommendedStreamConfigurationMap(
823                     RecommendedStreamConfigurationMap.USECASE_LOW_LATENCY_SNAPSHOT);
824             if ((previewConfig == null) && (videoRecordingConfig == null) &&
825                     (videoSnapshotConfig == null) && (snapshotConfig == null) &&
826                     (rawConfig == null) && (zslConfig == null) && (lowLatencyConfig == null)) {
827                 Log.i(TAG, "Camera " + mAllCameraIds[i] +
828                         " doesn't support recommended configurations, skipping test");
829                 continue;
830             }
831 
832             assertNotNull(String.format("Mandatory recommended preview configuration map not " +
833                     "found for: ID %s", mAllCameraIds[i]), previewConfig);
834             verifyRecommendedPreviewConfiguration(mAllCameraIds[i], c, previewConfig);
835 
836             assertNotNull(String.format("Mandatory recommended video recording configuration map " +
837                     "not found for: ID %s", mAllCameraIds[i]), videoRecordingConfig);
838             verifyRecommendedVideoConfiguration(mAllCameraIds[i], c, videoRecordingConfig);
839 
840             assertNotNull(String.format("Mandatory recommended video snapshot configuration map " +
841                     "not found for: ID %s", mAllCameraIds[i]), videoSnapshotConfig);
842             verifyRecommendedVideoSnapshotConfiguration(mAllCameraIds[i], c, videoSnapshotConfig,
843                     videoRecordingConfig);
844 
845             assertNotNull(String.format("Mandatory recommended snapshot configuration map not " +
846                     "found for: ID %s", mAllCameraIds[i]), snapshotConfig);
847             verifyRecommendedSnapshotConfiguration(mAllCameraIds[i], c, snapshotConfig);
848 
849             if (arrayContains(actualCapabilities, RAW)) {
850                 assertNotNull(String.format("Mandatory recommended raw configuration map not " +
851                         "found for: ID %s", mAllCameraIds[i]), rawConfig);
852                 verifyRecommendedRawConfiguration(mAllCameraIds[i], c, rawConfig);
853             }
854 
855             if (arrayContains(actualCapabilities, OPAQUE_REPROCESS) ||
856                     arrayContains(actualCapabilities, YUV_REPROCESS)) {
857                 assertNotNull(String.format("Mandatory recommended ZSL configuration map not " +
858                         "found for: ID %s", mAllCameraIds[i]), zslConfig);
859                 verifyRecommendedZSLConfiguration(mAllCameraIds[i], c, zslConfig);
860             }
861 
862             if (lowLatencyConfig != null) {
863                 verifyRecommendedLowLatencyConfiguration(mAllCameraIds[i], c, lowLatencyConfig);
864             }
865         }
866     }
867 
868     /**
869      * Test {@link CameraCharacteristics#getKeys}
870      */
testKeys()871     public void testKeys() {
872         for (int i = 0; i < mAllCameraIds.length; i++) {
873             CameraCharacteristics c = mCharacteristics.get(i);
874             mCollector.setCameraId(mAllCameraIds[i]);
875 
876             if (VERBOSE) {
877                 Log.v(TAG, "testKeys - testing characteristics for camera " + mAllCameraIds[i]);
878             }
879 
880             List<CameraCharacteristics.Key<?>> allKeys = c.getKeys();
881             assertNotNull("Camera characteristics keys must not be null", allKeys);
882             assertFalse("Camera characteristics keys must have at least 1 key",
883                     allKeys.isEmpty());
884 
885             for (CameraCharacteristics.Key<?> key : allKeys) {
886                 assertKeyPrefixValid(key.getName());
887 
888                 // All characteristics keys listed must never be null
889                 mCollector.expectKeyValueNotNull(c, key);
890 
891                 // TODO: add a check that key must not be @hide
892             }
893 
894             /*
895              * List of keys that must be present in camera characteristics (not null).
896              *
897              * Keys for LIMITED, FULL devices might be available despite lacking either
898              * the hardware level or the capability. This is *OK*. This only lists the
899              * *minimal* requirements for a key to be listed.
900              *
901              * LEGACY devices are a bit special since they map to api1 devices, so we know
902              * for a fact most keys are going to be illegal there so they should never be
903              * available.
904              *
905              * For LIMITED-level keys, if the level is >= LIMITED, then the capabilities are used to
906              * do the actual checking.
907              */
908             {
909                 //                                           (Key Name)                                     (HW Level)  (Capabilities <Var-Arg>)
910                 expectKeyAvailable(c, CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES     , OPT      ,   BC                   );
911                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_MODES                         , OPT      ,   BC                   );
912                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES          , OPT      ,   BC                   );
913                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES                      , OPT      ,   BC                   );
914                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES          , OPT      ,   BC                   );
915                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE                   , OPT      ,   BC                   );
916                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP                    , OPT      ,   BC                   );
917                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE                       , OPT      ,   BC                   );
918                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES                      , OPT      ,   BC                   );
919                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS                       , OPT      ,   BC                   );
920                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES                   , OPT      ,   BC                   );
921                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES     , OPT      ,   BC                   );
922                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES                     , OPT      ,   BC                   );
923                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE                      , OPT      ,   BC                   );
924                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AE                          , OPT      ,   BC                   );
925                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AF                          , OPT      ,   BC                   );
926                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB                         , OPT      ,   BC                   );
927                 expectKeyAvailable(c, CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES                       , FULL     ,   NONE                 );
928                 expectKeyAvailable(c, CameraCharacteristics.FLASH_INFO_AVAILABLE                            , OPT      ,   BC                   );
929                 expectKeyAvailable(c, CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES             , OPT      ,   RAW                  );
930                 expectKeyAvailable(c, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL                   , OPT      ,   BC                   );
931                 expectKeyAvailable(c, CameraCharacteristics.INFO_VERSION                                    , OPT      ,   NONE                 );
932                 expectKeyAvailable(c, CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES                  , OPT      ,   BC                   );
933                 expectKeyAvailable(c, CameraCharacteristics.LENS_FACING                                     , OPT      ,   BC                   );
934                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES                   , FULL     ,   MANUAL_SENSOR        );
935                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES            , FULL     ,   MANUAL_SENSOR        );
936                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION       , LIMITED  ,   BC                   );
937                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION            , LIMITED  ,   MANUAL_SENSOR        );
938                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE                   , LIMITED  ,   BC                   );
939                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE                , LIMITED  ,   BC                   );
940                 expectKeyAvailable(c, CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES , OPT      ,   BC                   );
941                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES                  , OPT      ,   BC                   );
942                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS                   , OPT      ,   YUV_REPROCESS, OPAQUE_REPROCESS);
943                 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP                 , OPT      ,   CONSTRAINED_HIGH_SPEED);
944                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC                     , OPT      ,   BC                   );
945                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING            , OPT      ,   BC                   );
946                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW                      , OPT      ,   BC                   );
947                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT                    , OPT      ,   BC                   );
948                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH                      , OPT      ,   BC                   );
949                 expectKeyAvailable(c, CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM               , OPT      ,   BC                   );
950                 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP                 , OPT      ,   BC                   );
951                 expectKeyAvailable(c, CameraCharacteristics.SCALER_CROPPING_TYPE                            , OPT      ,   BC                   );
952                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN                      , FULL     ,   RAW                  );
953                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE                   , OPT      ,   BC, RAW              );
954                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT            , FULL     ,   RAW                  );
955                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE                 , FULL     ,   MANUAL_SENSOR        );
956                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION                  , FULL     ,   MANUAL_SENSOR        );
957                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE                    , OPT      ,   BC                   );
958                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE                   , FULL     ,   MANUAL_SENSOR        );
959                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL                         , OPT      ,   RAW                  );
960                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE                    , OPT      ,   BC                   );
961                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY                   , FULL     ,   MANUAL_SENSOR        );
962                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_ORIENTATION                              , OPT      ,   BC                   );
963                 expectKeyAvailable(c, CameraCharacteristics.SHADING_AVAILABLE_MODES                         , LIMITED  ,   MANUAL_POSTPROC, RAW );
964                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES     , OPT      ,   BC                   );
965                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES   , OPT      ,   RAW                  );
966                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES, LIMITED  ,   RAW                  );
967                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT                  , OPT      ,   BC                   );
968                 expectKeyAvailable(c, CameraCharacteristics.SYNC_MAX_LATENCY                                , OPT      ,   BC                   );
969                 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES                , FULL     ,   MANUAL_POSTPROC      );
970                 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS                        , FULL     ,   MANUAL_POSTPROC      );
971 
972                 // Future: Use column editors for modifying above, ignore line length to keep 1 key per line
973 
974                 // TODO: check that no other 'android' keys are listed in #getKeys if they aren't in the above list
975             }
976 
977             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
978             assertNotNull("android.request.availableCapabilities must never be null",
979                     actualCapabilities);
980             boolean isMonochrome = arrayContains(actualCapabilities,
981                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME);
982             if (!isMonochrome) {
983                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1                   , OPT      ,   RAW                  );
984                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM1                         , OPT      ,   RAW                  );
985                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX1                          , OPT      ,   RAW                  );
986                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1                    , OPT      ,   RAW                  );
987 
988 
989                 // Only check for these if the second reference illuminant is included
990                 if (allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2)) {
991                     expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2                    , OPT      ,   RAW                  );
992                     expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM2                         , OPT      ,   RAW                  );
993                     expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2                   , OPT      ,   RAW                  );
994                     expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX2                          , OPT      ,   RAW                  );
995                 }
996             }
997 
998             // Required key if any of RAW format output is supported
999             StreamConfigurationMap config =
1000                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1001             assertNotNull(String.format("No stream configuration map found for: ID %s",
1002                     mAllCameraIds[i]), config);
1003             if (config.isOutputSupportedFor(ImageFormat.RAW_SENSOR) ||
1004                     config.isOutputSupportedFor(ImageFormat.RAW10)  ||
1005                     config.isOutputSupportedFor(ImageFormat.RAW12)  ||
1006                     config.isOutputSupportedFor(ImageFormat.RAW_PRIVATE)) {
1007                 expectKeyAvailable(c,
1008                         CameraCharacteristics.CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE, OPT, BC);
1009             }
1010 
1011             // External Camera exceptional keys
1012             Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
1013             boolean isExternalCamera = (hwLevel ==
1014                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
1015             if (!isExternalCamera) {
1016                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS               , OPT      ,   BC                   );
1017                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES             , OPT      ,   BC                   );
1018                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE                       , OPT      ,   BC                   );
1019             }
1020 
1021 
1022             // Verify version is a short text string.
1023             if (allKeys.contains(CameraCharacteristics.INFO_VERSION)) {
1024                 final String TEXT_REGEX = "[\\p{Alnum}\\p{Punct}\\p{Space}]*";
1025                 final int MAX_VERSION_LENGTH = 256;
1026 
1027                 String version = c.get(CameraCharacteristics.INFO_VERSION);
1028                 mCollector.expectTrue("Version contains non-text characters: " + version,
1029                         version.matches(TEXT_REGEX));
1030                 mCollector.expectLessOrEqual("Version too long: " + version, MAX_VERSION_LENGTH,
1031                         version.length());
1032             }
1033         }
1034     }
1035 
1036     /**
1037      * Test values for static metadata used by the RAW capability.
1038      */
testStaticRawCharacteristics()1039     public void testStaticRawCharacteristics() {
1040         for (int i = 0; i < mAllCameraIds.length; i++) {
1041             CameraCharacteristics c = mCharacteristics.get(i);
1042             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1043             assertNotNull("android.request.availableCapabilities must never be null",
1044                     actualCapabilities);
1045             if (!arrayContains(actualCapabilities,
1046                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
1047                 Log.i(TAG, "RAW capability is not supported in camera " + mAllCameraIds[i] +
1048                         ". Skip the test.");
1049                 continue;
1050             }
1051 
1052             Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
1053             if (actualHwLevel != null && actualHwLevel == FULL) {
1054                 mCollector.expectKeyValueContains(c,
1055                         CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES,
1056                         CameraCharacteristics.HOT_PIXEL_MODE_FAST);
1057             }
1058             mCollector.expectKeyValueContains(c,
1059                     CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES, false);
1060             mCollector.expectKeyValueGreaterThan(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL,
1061                     MIN_ALLOWABLE_WHITELEVEL);
1062 
1063 
1064             boolean isMonochrome = arrayContains(actualCapabilities,
1065                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME);
1066             if (!isMonochrome) {
1067                 mCollector.expectKeyValueIsIn(c,
1068                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
1069                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB,
1070                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG,
1071                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG,
1072                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR);
1073                 // TODO: SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB isn't supported yet.
1074 
1075                 mCollector.expectKeyValueInRange(c,
1076                         CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1,
1077                         CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT,
1078                         CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN);
1079                 // Only check the range if the second reference illuminant is avaliable
1080                 if (c.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2) != null) {
1081                         mCollector.expectKeyValueInRange(c,
1082                         CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2,
1083                         (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT,
1084                         (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN);
1085                 }
1086 
1087                 Rational[] zeroes = new Rational[9];
1088                 Arrays.fill(zeroes, Rational.ZERO);
1089 
1090                 ColorSpaceTransform zeroed = new ColorSpaceTransform(zeroes);
1091                 mCollector.expectNotEquals("Forward Matrix1 should not contain all zeroes.", zeroed,
1092                         c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX1));
1093                 mCollector.expectNotEquals("Forward Matrix2 should not contain all zeroes.", zeroed,
1094                         c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX2));
1095                 mCollector.expectNotEquals("Calibration Transform1 should not contain all zeroes.",
1096                         zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1));
1097                 mCollector.expectNotEquals("Calibration Transform2 should not contain all zeroes.",
1098                         zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2));
1099                 mCollector.expectNotEquals("Color Transform1 should not contain all zeroes.",
1100                         zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1));
1101                 mCollector.expectNotEquals("Color Transform2 should not contain all zeroes.",
1102                         zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2));
1103             } else {
1104                 mCollector.expectKeyValueIsIn(c,
1105                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
1106                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO,
1107                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR);
1108                 // TODO: SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB isn't supported yet.
1109             }
1110 
1111             BlackLevelPattern blackLevel = mCollector.expectKeyValueNotNull(c,
1112                     CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN);
1113             if (blackLevel != null) {
1114                 String blackLevelPatternString = blackLevel.toString();
1115                 if (VERBOSE) {
1116                     Log.v(TAG, "Black level pattern: " + blackLevelPatternString);
1117                 }
1118                 int[] blackLevelPattern = new int[BlackLevelPattern.COUNT];
1119                 blackLevel.copyTo(blackLevelPattern, /*offset*/0);
1120                 if (isMonochrome) {
1121                     for (int index = 1; index < BlackLevelPattern.COUNT; index++) {
1122                         mCollector.expectEquals(
1123                                 "Monochrome camera 2x2 channels blacklevel value must be the same.",
1124                                 blackLevelPattern[index], blackLevelPattern[0]);
1125                     }
1126                 }
1127 
1128                 Integer whitelevel = c.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
1129                 if (whitelevel != null) {
1130                     mCollector.expectValuesInRange("BlackLevelPattern", blackLevelPattern, 0,
1131                             whitelevel);
1132                 } else {
1133                     mCollector.addMessage(
1134                             "No WhiteLevel available, cannot check BlackLevelPattern range.");
1135                 }
1136             }
1137 
1138             // TODO: profileHueSatMap, and profileToneCurve aren't supported yet.
1139         }
1140     }
1141 
1142     /**
1143      * Test values for the available session keys.
1144      */
testStaticSessionKeys()1145     public void testStaticSessionKeys() throws Exception {
1146         for (CameraCharacteristics c : mCharacteristics) {
1147             List<CaptureRequest.Key<?>> availableSessionKeys = c.getAvailableSessionKeys();
1148             if (availableSessionKeys == null) {
1149                 continue;
1150             }
1151             List<CaptureRequest.Key<?>> availableRequestKeys = c.getAvailableCaptureRequestKeys();
1152 
1153             //Every session key should be part of the available request keys
1154             for (CaptureRequest.Key<?> key : availableSessionKeys) {
1155                 assertTrue("Session key:" + key.getName() + " not present in the available capture "
1156                         + "request keys!", availableRequestKeys.contains(key));
1157             }
1158         }
1159     }
1160 
1161     /**
1162      * Test values for static metadata used by the BURST capability.
1163      */
testStaticBurstCharacteristics()1164     public void testStaticBurstCharacteristics() throws Exception {
1165         for (int i = 0; i < mAllCameraIds.length; i++) {
1166             CameraCharacteristics c = mCharacteristics.get(i);
1167             int[] actualCapabilities = CameraTestUtils.getValueNotNull(
1168                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1169 
1170             // Check if the burst capability is defined
1171             boolean haveBurstCapability = arrayContains(actualCapabilities,
1172                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
1173             boolean haveBC = arrayContains(actualCapabilities,
1174                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
1175 
1176             if(haveBurstCapability && !haveBC) {
1177                 fail("Must have BACKWARD_COMPATIBLE capability if BURST_CAPTURE capability is defined");
1178             }
1179 
1180             if (!haveBC) continue;
1181 
1182             StreamConfigurationMap config =
1183                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1184             assertNotNull(String.format("No stream configuration map found for: ID %s",
1185                     mAllCameraIds[i]), config);
1186             Rect activeRect = CameraTestUtils.getValueNotNull(
1187                     c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
1188             Size sensorSize = new Size(activeRect.width(), activeRect.height());
1189 
1190             // Ensure that max YUV size matches max JPEG size
1191             Size maxYuvSize = CameraTestUtils.getMaxSize(
1192                     config.getOutputSizes(ImageFormat.YUV_420_888));
1193             Size maxFastYuvSize = maxYuvSize;
1194 
1195             Size[] slowYuvSizes = config.getHighResolutionOutputSizes(ImageFormat.YUV_420_888);
1196             Size maxSlowYuvSizeLessThan24M = null;
1197             if (haveBurstCapability && slowYuvSizes != null && slowYuvSizes.length > 0) {
1198                 Size maxSlowYuvSize = CameraTestUtils.getMaxSize(slowYuvSizes);
1199                 final int SIZE_24MP_BOUND = 24000000;
1200                 maxSlowYuvSizeLessThan24M =
1201                         CameraTestUtils.getMaxSizeWithBound(slowYuvSizes, SIZE_24MP_BOUND);
1202                 maxYuvSize = CameraTestUtils.getMaxSize(new Size[]{maxYuvSize, maxSlowYuvSize});
1203             }
1204 
1205             Size maxJpegSize = CameraTestUtils.getMaxSize(CameraTestUtils.getSupportedSizeForFormat(
1206                     ImageFormat.JPEG, mAllCameraIds[i], mCameraManager));
1207 
1208             boolean haveMaxYuv = maxYuvSize != null ?
1209                 (maxJpegSize.getWidth() <= maxYuvSize.getWidth() &&
1210                         maxJpegSize.getHeight() <= maxYuvSize.getHeight()) : false;
1211 
1212             Pair<Boolean, Size> maxYuvMatchSensorPair = isSizeWithinSensorMargin(maxYuvSize,
1213                     sensorSize);
1214 
1215             // No need to do null check since framework will generate the key if HAL don't supply
1216             boolean haveAeLock = CameraTestUtils.getValueNotNull(
1217                     c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE);
1218             boolean haveAwbLock = CameraTestUtils.getValueNotNull(
1219                     c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE);
1220 
1221             // Ensure that some >=8MP YUV output is fast enough - needs to be at least 20 fps
1222 
1223             long maxFastYuvRate =
1224                     config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxFastYuvSize);
1225             final long MIN_8MP_DURATION_BOUND_NS = 50000000; // 50 ms, 20 fps
1226             boolean haveFastYuvRate = maxFastYuvRate <= MIN_8MP_DURATION_BOUND_NS;
1227 
1228             final int SIZE_8MP_BOUND = 8000000;
1229             boolean havefast8MPYuv = (maxFastYuvSize.getWidth() * maxFastYuvSize.getHeight()) >
1230                     SIZE_8MP_BOUND;
1231 
1232             // Ensure that max YUV output smaller than 24MP is fast enough
1233             // - needs to be at least 10 fps
1234             final long MIN_MAXSIZE_DURATION_BOUND_NS = 100000000; // 100 ms, 10 fps
1235             long maxYuvRate = maxFastYuvRate;
1236             if (maxSlowYuvSizeLessThan24M != null) {
1237                 maxYuvRate = config.getOutputMinFrameDuration(
1238                         ImageFormat.YUV_420_888, maxSlowYuvSizeLessThan24M);
1239             }
1240             boolean haveMaxYuvRate = maxYuvRate <= MIN_MAXSIZE_DURATION_BOUND_NS;
1241 
1242             // Ensure that there's an FPS range that's fast enough to capture at above
1243             // minFrameDuration, for full-auto bursts at the fast resolutions
1244             Range[] fpsRanges = CameraTestUtils.getValueNotNull(
1245                     c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
1246             float minYuvFps = 1.f / maxFastYuvRate;
1247 
1248             boolean haveFastAeTargetFps = false;
1249             for (Range<Integer> r : fpsRanges) {
1250                 if (r.getLower() >= minYuvFps) {
1251                     haveFastAeTargetFps = true;
1252                     break;
1253                 }
1254             }
1255 
1256             // Ensure that maximum sync latency is small enough for fast setting changes, even if
1257             // it's not quite per-frame
1258 
1259             Integer maxSyncLatencyValue = c.get(CameraCharacteristics.SYNC_MAX_LATENCY);
1260             assertNotNull(String.format("No sync latency declared for ID %s", mAllCameraIds[i]),
1261                     maxSyncLatencyValue);
1262 
1263             int maxSyncLatency = maxSyncLatencyValue;
1264             final long MAX_LATENCY_BOUND = 4;
1265             boolean haveFastSyncLatency =
1266                 (maxSyncLatency <= MAX_LATENCY_BOUND) && (maxSyncLatency >= 0);
1267 
1268             if (haveBurstCapability) {
1269                 assertTrue("Must have slow YUV size array when BURST_CAPTURE capability is defined!",
1270                         slowYuvSizes != null);
1271                 assertTrue(
1272                         String.format("BURST-capable camera device %s does not have maximum YUV " +
1273                                 "size that is at least max JPEG size",
1274                                 mAllCameraIds[i]),
1275                         haveMaxYuv);
1276                 assertTrue(
1277                         String.format("BURST-capable camera device %s max-resolution " +
1278                                 "YUV frame rate is too slow" +
1279                                 "(%d ns min frame duration reported, less than %d ns expected)",
1280                                 mAllCameraIds[i], maxYuvRate, MIN_MAXSIZE_DURATION_BOUND_NS),
1281                         haveMaxYuvRate);
1282                 assertTrue(
1283                         String.format("BURST-capable camera device %s >= 8MP YUV output " +
1284                                 "frame rate is too slow" +
1285                                 "(%d ns min frame duration reported, less than %d ns expected)",
1286                                 mAllCameraIds[i], maxYuvRate, MIN_8MP_DURATION_BOUND_NS),
1287                         haveFastYuvRate);
1288                 assertTrue(
1289                         String.format("BURST-capable camera device %s does not list an AE target " +
1290                                 " FPS range with min FPS >= %f, for full-AUTO bursts",
1291                                 mAllCameraIds[i], minYuvFps),
1292                         haveFastAeTargetFps);
1293                 assertTrue(
1294                         String.format("BURST-capable camera device %s YUV sync latency is too long" +
1295                                 "(%d frames reported, [0, %d] frames expected)",
1296                                 mAllCameraIds[i], maxSyncLatency, MAX_LATENCY_BOUND),
1297                         haveFastSyncLatency);
1298                 assertTrue(
1299                         String.format("BURST-capable camera device %s max YUV size %s should be" +
1300                                 "close to active array size %s or cropped active array size %s",
1301                                 mAllCameraIds[i], maxYuvSize.toString(), sensorSize.toString(),
1302                                 maxYuvMatchSensorPair.second.toString()),
1303                         maxYuvMatchSensorPair.first.booleanValue());
1304                 assertTrue(
1305                         String.format("BURST-capable camera device %s does not support AE lock",
1306                                 mAllCameraIds[i]),
1307                         haveAeLock);
1308                 assertTrue(
1309                         String.format("BURST-capable camera device %s does not support AWB lock",
1310                                 mAllCameraIds[i]),
1311                         haveAwbLock);
1312             } else {
1313                 assertTrue("Must have null slow YUV size array when no BURST_CAPTURE capability!",
1314                         slowYuvSizes == null);
1315                 assertTrue(
1316                         String.format("Camera device %s has all the requirements for BURST" +
1317                                 " capability but does not report it!", mAllCameraIds[i]),
1318                         !(haveMaxYuv && haveMaxYuvRate && haveFastYuvRate && haveFastAeTargetFps &&
1319                                 haveFastSyncLatency && maxYuvMatchSensorPair.first.booleanValue() &&
1320                                 haveAeLock && haveAwbLock));
1321             }
1322         }
1323     }
1324 
1325     /**
1326      * Check reprocessing capabilities.
1327      */
testReprocessingCharacteristics()1328     public void testReprocessingCharacteristics() {
1329         for (int i = 0; i < mAllCameraIds.length; i++) {
1330             Log.i(TAG, "testReprocessingCharacteristics: Testing camera ID " + mAllCameraIds[i]);
1331 
1332             CameraCharacteristics c = mCharacteristics.get(i);
1333             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1334             assertNotNull("android.request.availableCapabilities must never be null",
1335                     capabilities);
1336             boolean supportYUV = arrayContains(capabilities,
1337                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
1338             boolean supportOpaque = arrayContains(capabilities,
1339                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
1340             StreamConfigurationMap configs =
1341                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1342             Integer maxNumInputStreams =
1343                     c.get(CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS);
1344             int[] availableEdgeModes = c.get(CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES);
1345             int[] availableNoiseReductionModes = c.get(
1346                     CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES);
1347 
1348             int[] inputFormats = configs.getInputFormats();
1349             int[] outputFormats = configs.getOutputFormats();
1350             boolean isMonochromeWithY8 = arrayContains(capabilities, MONOCHROME)
1351                     && arrayContains(outputFormats, ImageFormat.Y8);
1352 
1353             boolean supportZslEdgeMode = false;
1354             boolean supportZslNoiseReductionMode = false;
1355             boolean supportHiQNoiseReductionMode = false;
1356             boolean supportHiQEdgeMode = false;
1357 
1358             if (availableEdgeModes != null) {
1359                 supportZslEdgeMode = Arrays.asList(CameraTestUtils.toObject(availableEdgeModes)).
1360                         contains(CaptureRequest.EDGE_MODE_ZERO_SHUTTER_LAG);
1361                 supportHiQEdgeMode = Arrays.asList(CameraTestUtils.toObject(availableEdgeModes)).
1362                         contains(CaptureRequest.EDGE_MODE_HIGH_QUALITY);
1363             }
1364 
1365             if (availableNoiseReductionModes != null) {
1366                 supportZslNoiseReductionMode = Arrays.asList(
1367                         CameraTestUtils.toObject(availableNoiseReductionModes)).contains(
1368                         CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG);
1369                 supportHiQNoiseReductionMode = Arrays.asList(
1370                         CameraTestUtils.toObject(availableNoiseReductionModes)).contains(
1371                         CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
1372             }
1373 
1374             if (supportYUV || supportOpaque) {
1375                 mCollector.expectTrue("Support reprocessing but max number of input stream is " +
1376                         maxNumInputStreams, maxNumInputStreams != null && maxNumInputStreams > 0);
1377                 mCollector.expectTrue("Support reprocessing but EDGE_MODE_ZERO_SHUTTER_LAG is " +
1378                         "not supported", supportZslEdgeMode);
1379                 mCollector.expectTrue("Support reprocessing but " +
1380                         "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is not supported",
1381                         supportZslNoiseReductionMode);
1382 
1383                 // For reprocessing, if we only require OFF and ZSL mode, it will be just like jpeg
1384                 // encoding. We implicitly require FAST to make reprocessing meaningful, which means
1385                 // that we also require HIGH_QUALITY.
1386                 mCollector.expectTrue("Support reprocessing but EDGE_MODE_HIGH_QUALITY is " +
1387                         "not supported", supportHiQEdgeMode);
1388                 mCollector.expectTrue("Support reprocessing but " +
1389                         "NOISE_REDUCTION_MODE_HIGH_QUALITY is not supported",
1390                         supportHiQNoiseReductionMode);
1391 
1392                 // Verify mandatory input formats are supported
1393                 mCollector.expectTrue("YUV_420_888 input must be supported for YUV reprocessing",
1394                         !supportYUV || arrayContains(inputFormats, ImageFormat.YUV_420_888));
1395                 mCollector.expectTrue("Y8 input must be supported for YUV reprocessing on " +
1396                         "MONOCHROME devices with Y8 support", !supportYUV || !isMonochromeWithY8
1397                         || arrayContains(inputFormats, ImageFormat.Y8));
1398                 mCollector.expectTrue("PRIVATE input must be supported for OPAQUE reprocessing",
1399                         !supportOpaque || arrayContains(inputFormats, ImageFormat.PRIVATE));
1400 
1401                 // max capture stall must be reported if one of the reprocessing is supported.
1402                 final int MAX_ALLOWED_STALL_FRAMES = 4;
1403                 Integer maxCaptureStall = c.get(CameraCharacteristics.REPROCESS_MAX_CAPTURE_STALL);
1404                 mCollector.expectTrue("max capture stall must be non-null and no larger than "
1405                         + MAX_ALLOWED_STALL_FRAMES,
1406                         maxCaptureStall != null && maxCaptureStall <= MAX_ALLOWED_STALL_FRAMES);
1407 
1408                 for (int input : inputFormats) {
1409                     // Verify mandatory output formats are supported
1410                     int[] outputFormatsForInput = configs.getValidOutputFormatsForInput(input);
1411                     mCollector.expectTrue(
1412                         "YUV_420_888 output must be supported for reprocessing",
1413                         input == ImageFormat.Y8
1414                         || arrayContains(outputFormatsForInput, ImageFormat.YUV_420_888));
1415                     mCollector.expectTrue(
1416                         "Y8 output must be supported for reprocessing on MONOCHROME devices with"
1417                         + " Y8 support", !isMonochromeWithY8 || input == ImageFormat.YUV_420_888
1418                         || arrayContains(outputFormatsForInput, ImageFormat.Y8));
1419                     mCollector.expectTrue("JPEG output must be supported for reprocessing",
1420                             arrayContains(outputFormatsForInput, ImageFormat.JPEG));
1421 
1422                     // Verify camera can output the reprocess input formats and sizes.
1423                     Size[] inputSizes = configs.getInputSizes(input);
1424                     Size[] outputSizes = configs.getOutputSizes(input);
1425                     Size[] highResOutputSizes = configs.getHighResolutionOutputSizes(input);
1426                     mCollector.expectTrue("no input size supported for format " + input,
1427                             inputSizes.length > 0);
1428                     mCollector.expectTrue("no output size supported for format " + input,
1429                             outputSizes.length > 0);
1430 
1431                     for (Size inputSize : inputSizes) {
1432                         mCollector.expectTrue("Camera must be able to output the supported " +
1433                                 "reprocessing input size",
1434                                 arrayContains(outputSizes, inputSize) ||
1435                                 arrayContains(highResOutputSizes, inputSize));
1436                     }
1437                 }
1438             } else {
1439                 mCollector.expectTrue("Doesn't support reprocessing but report input format: " +
1440                         Arrays.toString(inputFormats), inputFormats.length == 0);
1441                 mCollector.expectTrue("Doesn't support reprocessing but max number of input " +
1442                         "stream is " + maxNumInputStreams,
1443                         maxNumInputStreams == null || maxNumInputStreams == 0);
1444                 mCollector.expectTrue("Doesn't support reprocessing but " +
1445                         "EDGE_MODE_ZERO_SHUTTER_LAG is supported", !supportZslEdgeMode);
1446                 mCollector.expectTrue("Doesn't support reprocessing but " +
1447                         "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is supported",
1448                         !supportZslNoiseReductionMode);
1449             }
1450         }
1451     }
1452 
1453     /**
1454      * Check depth output capability
1455      */
testDepthOutputCharacteristics()1456     public void testDepthOutputCharacteristics() {
1457         for (int i = 0; i < mAllCameraIds.length; i++) {
1458             Log.i(TAG, "testDepthOutputCharacteristics: Testing camera ID " + mAllCameraIds[i]);
1459 
1460             CameraCharacteristics c = mCharacteristics.get(i);
1461             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1462             assertNotNull("android.request.availableCapabilities must never be null",
1463                     capabilities);
1464             boolean supportDepth = arrayContains(capabilities,
1465                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT);
1466             StreamConfigurationMap configs =
1467                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1468 
1469             int[] outputFormats = configs.getOutputFormats();
1470             boolean hasDepth16 = arrayContains(outputFormats, ImageFormat.DEPTH16);
1471 
1472             Boolean depthIsExclusive = c.get(CameraCharacteristics.DEPTH_DEPTH_IS_EXCLUSIVE);
1473 
1474             float[] poseRotation = c.get(CameraCharacteristics.LENS_POSE_ROTATION);
1475             float[] poseTranslation = c.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
1476             Integer poseReference = c.get(CameraCharacteristics.LENS_POSE_REFERENCE);
1477             float[] cameraIntrinsics = c.get(CameraCharacteristics.LENS_INTRINSIC_CALIBRATION);
1478             float[] distortion = getLensDistortion(c);
1479             Size pixelArraySize = c.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
1480             Rect precorrectionArray = c.get(
1481                 CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1482             Rect activeArray = c.get(
1483                 CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
1484             float jpegAspectRatioThreshold = .01f;
1485             boolean jpegSizeMatch = false;
1486 
1487             // Verify pre-correction array encloses active array
1488             mCollector.expectTrue("preCorrectionArray [" + precorrectionArray.left + ", " +
1489                     precorrectionArray.top + ", " + precorrectionArray.right + ", " +
1490                     precorrectionArray.bottom + "] does not enclose activeArray[" +
1491                     activeArray.left + ", " + activeArray.top + ", " + activeArray.right +
1492                     ", " + activeArray.bottom,
1493                     precorrectionArray.contains(activeArray.left, activeArray.top) &&
1494                     precorrectionArray.contains(activeArray.right-1, activeArray.bottom-1));
1495 
1496             // Verify pixel array encloses pre-correction array
1497             mCollector.expectTrue("preCorrectionArray [" + precorrectionArray.left + ", " +
1498                     precorrectionArray.top + ", " + precorrectionArray.right + ", " +
1499                     precorrectionArray.bottom + "] isn't enclosed by pixelArray[" +
1500                     pixelArraySize.getWidth() + ", " + pixelArraySize.getHeight() + "]",
1501                     precorrectionArray.left >= 0 &&
1502                     precorrectionArray.left < pixelArraySize.getWidth() &&
1503                     precorrectionArray.right > 0 &&
1504                     precorrectionArray.right <= pixelArraySize.getWidth() &&
1505                     precorrectionArray.top >= 0 &&
1506                     precorrectionArray.top < pixelArraySize.getHeight() &&
1507                     precorrectionArray.bottom > 0 &&
1508                     precorrectionArray.bottom <= pixelArraySize.getHeight());
1509 
1510             if (supportDepth) {
1511                 mCollector.expectTrue("Supports DEPTH_OUTPUT but does not support DEPTH16",
1512                         hasDepth16);
1513                 if (hasDepth16) {
1514                     Size[] depthSizes = configs.getOutputSizes(ImageFormat.DEPTH16);
1515                     Size[] jpegSizes = configs.getOutputSizes(ImageFormat.JPEG);
1516                     mCollector.expectTrue("Supports DEPTH_OUTPUT but no sizes for DEPTH16 supported!",
1517                             depthSizes != null && depthSizes.length > 0);
1518                     if (depthSizes != null) {
1519                         for (Size depthSize : depthSizes) {
1520                             mCollector.expectTrue("All depth16 sizes must be positive",
1521                                     depthSize.getWidth() > 0 && depthSize.getHeight() > 0);
1522                             long minFrameDuration = configs.getOutputMinFrameDuration(
1523                                     ImageFormat.DEPTH16, depthSize);
1524                             mCollector.expectTrue("Non-negative min frame duration for depth size "
1525                                     + depthSize + " expected, got " + minFrameDuration,
1526                                     minFrameDuration >= 0);
1527                             long stallDuration = configs.getOutputStallDuration(
1528                                     ImageFormat.DEPTH16, depthSize);
1529                             mCollector.expectTrue("Non-negative stall duration for depth size "
1530                                     + depthSize + " expected, got " + stallDuration,
1531                                     stallDuration >= 0);
1532                             if ((jpegSizes != null) && (!jpegSizeMatch)) {
1533                                 for (Size jpegSize : jpegSizes) {
1534                                     if (jpegSize.equals(depthSize)) {
1535                                         jpegSizeMatch = true;
1536                                         break;
1537                                     } else {
1538                                         float depthAR = (float) depthSize.getWidth() /
1539                                                 (float) depthSize.getHeight();
1540                                         float jpegAR = (float) jpegSize.getWidth() /
1541                                                 (float) jpegSize.getHeight();
1542                                         if (Math.abs(depthAR - jpegAR) <=
1543                                                 jpegAspectRatioThreshold) {
1544                                             jpegSizeMatch = true;
1545                                             break;
1546                                         }
1547                                     }
1548                                 }
1549                             }
1550                         }
1551                     }
1552                 }
1553                 if (arrayContains(outputFormats, ImageFormat.DEPTH_POINT_CLOUD)) {
1554                     Size[] depthCloudSizes = configs.getOutputSizes(ImageFormat.DEPTH_POINT_CLOUD);
1555                     mCollector.expectTrue("Supports DEPTH_POINT_CLOUD " +
1556                             "but no sizes for DEPTH_POINT_CLOUD supported!",
1557                             depthCloudSizes != null && depthCloudSizes.length > 0);
1558                     if (depthCloudSizes != null) {
1559                         for (Size depthCloudSize : depthCloudSizes) {
1560                             mCollector.expectTrue("All depth point cloud sizes must be nonzero",
1561                                     depthCloudSize.getWidth() > 0);
1562                             mCollector.expectTrue("All depth point cloud sizes must be N x 1",
1563                                     depthCloudSize.getHeight() == 1);
1564                             long minFrameDuration = configs.getOutputMinFrameDuration(
1565                                     ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
1566                             mCollector.expectTrue("Non-negative min frame duration for depth size "
1567                                     + depthCloudSize + " expected, got " + minFrameDuration,
1568                                     minFrameDuration >= 0);
1569                             long stallDuration = configs.getOutputStallDuration(
1570                                     ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
1571                             mCollector.expectTrue("Non-negative stall duration for depth size "
1572                                     + depthCloudSize + " expected, got " + stallDuration,
1573                                     stallDuration >= 0);
1574                         }
1575                     }
1576                 }
1577                 if (arrayContains(outputFormats, ImageFormat.DEPTH_JPEG)) {
1578                     mCollector.expectTrue("Supports DEPTH_JPEG but has no DEPTH16 support!",
1579                             hasDepth16);
1580                     mCollector.expectTrue("Supports DEPTH_JPEG but DEPTH_IS_EXCLUSIVE is not " +
1581                             "defined", depthIsExclusive != null);
1582                     mCollector.expectTrue("Supports DEPTH_JPEG but DEPTH_IS_EXCLUSIVE is true",
1583                             !depthIsExclusive.booleanValue());
1584                     Size[] depthJpegSizes = configs.getOutputSizes(ImageFormat.DEPTH_JPEG);
1585                     mCollector.expectTrue("Supports DEPTH_JPEG " +
1586                             "but no sizes for DEPTH_JPEG supported!",
1587                             depthJpegSizes != null && depthJpegSizes.length > 0);
1588                     mCollector.expectTrue("Supports DEPTH_JPEG but there are no JPEG sizes with" +
1589                             " matching DEPTH16 aspect ratio", jpegSizeMatch);
1590                     if (depthJpegSizes != null) {
1591                         for (Size depthJpegSize : depthJpegSizes) {
1592                             mCollector.expectTrue("All depth jpeg sizes must be nonzero",
1593                                     depthJpegSize.getWidth() > 0 && depthJpegSize.getHeight() > 0);
1594                             long minFrameDuration = configs.getOutputMinFrameDuration(
1595                                     ImageFormat.DEPTH_JPEG, depthJpegSize);
1596                             mCollector.expectTrue("Non-negative min frame duration for depth jpeg" +
1597                                    " size " + depthJpegSize + " expected, got " + minFrameDuration,
1598                                     minFrameDuration >= 0);
1599                             long stallDuration = configs.getOutputStallDuration(
1600                                     ImageFormat.DEPTH_JPEG, depthJpegSize);
1601                             mCollector.expectTrue("Non-negative stall duration for depth jpeg size "
1602                                     + depthJpegSize + " expected, got " + stallDuration,
1603                                     stallDuration >= 0);
1604                         }
1605                     }
1606                 } else {
1607                     boolean canSupportDynamicDepth = jpegSizeMatch && !depthIsExclusive;
1608                     mCollector.expectTrue("Device must support DEPTH_JPEG, please check whether " +
1609                             "library libdepthphoto.so is part of the device PRODUCT_PACKAGES",
1610                             !canSupportDynamicDepth);
1611                 }
1612 
1613 
1614                 mCollector.expectTrue("Supports DEPTH_OUTPUT but DEPTH_IS_EXCLUSIVE is not defined",
1615                         depthIsExclusive != null);
1616 
1617                 verifyLensCalibration(poseRotation, poseTranslation, poseReference,
1618                         cameraIntrinsics, distortion, precorrectionArray);
1619 
1620             } else {
1621                 boolean hasFields =
1622                     hasDepth16 && (poseTranslation != null) &&
1623                     (poseRotation != null) && (cameraIntrinsics != null) &&
1624                     (distortion != null) && (depthIsExclusive != null);
1625 
1626                 mCollector.expectTrue(
1627                         "All necessary depth fields defined, but DEPTH_OUTPUT capability is not listed",
1628                         !hasFields);
1629 
1630                 boolean reportCalibration = poseTranslation != null ||
1631                         poseRotation != null || cameraIntrinsics !=null;
1632                 // Verify calibration keys are co-existing
1633                 if (reportCalibration) {
1634                     mCollector.expectTrue(
1635                             "Calibration keys must be co-existing",
1636                             poseTranslation != null && poseRotation != null &&
1637                             cameraIntrinsics !=null);
1638                 }
1639 
1640                 boolean reportDistortion = distortion != null;
1641                 if (reportDistortion) {
1642                     mCollector.expectTrue(
1643                             "Calibration keys must present where distortion is reported",
1644                             reportCalibration);
1645                 }
1646             }
1647         }
1648     }
1649 
verifyLensCalibration(float[] poseRotation, float[] poseTranslation, Integer poseReference, float[] cameraIntrinsics, float[] distortion, Rect precorrectionArray)1650     private void verifyLensCalibration(float[] poseRotation, float[] poseTranslation,
1651             Integer poseReference, float[] cameraIntrinsics, float[] distortion,
1652             Rect precorrectionArray) {
1653 
1654         mCollector.expectTrue(
1655             "LENS_POSE_ROTATION not right size",
1656             poseRotation != null && poseRotation.length == 4);
1657         mCollector.expectTrue(
1658             "LENS_POSE_TRANSLATION not right size",
1659             poseTranslation != null && poseTranslation.length == 3);
1660         mCollector.expectTrue(
1661             "LENS_POSE_REFERENCE is not defined",
1662             poseReference != null);
1663         mCollector.expectTrue(
1664             "LENS_INTRINSIC_CALIBRATION not right size",
1665             cameraIntrinsics != null && cameraIntrinsics.length == 5);
1666         mCollector.expectTrue(
1667             "LENS_DISTORTION not right size",
1668             distortion != null && distortion.length == 6);
1669 
1670         if (poseRotation != null && poseRotation.length == 4) {
1671             float normSq =
1672                     poseRotation[0] * poseRotation[0] +
1673                     poseRotation[1] * poseRotation[1] +
1674                     poseRotation[2] * poseRotation[2] +
1675                     poseRotation[3] * poseRotation[3];
1676             mCollector.expectTrue(
1677                 "LENS_POSE_ROTATION quarternion must be unit-length",
1678                 0.9999f < normSq && normSq < 1.0001f);
1679 
1680             // TODO: Cross-validate orientation/facing and poseRotation
1681         }
1682 
1683         if (poseTranslation != null && poseTranslation.length == 3) {
1684             float normSq =
1685                     poseTranslation[0] * poseTranslation[0] +
1686                     poseTranslation[1] * poseTranslation[1] +
1687                     poseTranslation[2] * poseTranslation[2];
1688             mCollector.expectTrue("Pose translation is larger than 1 m",
1689                     normSq < 1.f);
1690         }
1691 
1692         if (poseReference != null) {
1693             int ref = poseReference;
1694             boolean validReference = false;
1695             switch (ref) {
1696                 case CameraCharacteristics.LENS_POSE_REFERENCE_PRIMARY_CAMERA:
1697                 case CameraCharacteristics.LENS_POSE_REFERENCE_GYROSCOPE:
1698                     // Allowed values
1699                     validReference = true;
1700                     break;
1701                 default:
1702             }
1703             mCollector.expectTrue("POSE_REFERENCE has unknown value", validReference);
1704         }
1705 
1706         mCollector.expectTrue("Does not have precorrection active array defined",
1707                 precorrectionArray != null);
1708 
1709         if (cameraIntrinsics != null && precorrectionArray != null) {
1710             float fx = cameraIntrinsics[0];
1711             float fy = cameraIntrinsics[1];
1712             float cx = cameraIntrinsics[2];
1713             float cy = cameraIntrinsics[3];
1714             float s = cameraIntrinsics[4];
1715             mCollector.expectTrue("Optical center expected to be within precorrection array",
1716                     0 <= cx && cx < precorrectionArray.width() &&
1717                     0 <= cy && cy < precorrectionArray.height());
1718 
1719             // TODO: Verify focal lengths and skew are reasonable
1720         }
1721 
1722         if (distortion != null) {
1723             // TODO: Verify radial distortion
1724         }
1725 
1726     }
1727 
1728     /**
1729      * Cross-check StreamConfigurationMap output
1730      */
testStreamConfigurationMap()1731     public void testStreamConfigurationMap() throws Exception {
1732         for (int i = 0; i < mAllCameraIds.length; i++) {
1733             Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + mAllCameraIds[i]);
1734             CameraCharacteristics c = mCharacteristics.get(i);
1735             StreamConfigurationMap config =
1736                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1737             assertNotNull(String.format("No stream configuration map found for: ID %s",
1738                             mAllCameraIds[i]), config);
1739 
1740             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1741             assertNotNull("android.request.availableCapabilities must never be null",
1742                     actualCapabilities);
1743 
1744             if (arrayContains(actualCapabilities, BC)) {
1745                 assertTrue("ImageReader must be supported",
1746                     config.isOutputSupportedFor(android.media.ImageReader.class));
1747                 assertTrue("MediaRecorder must be supported",
1748                     config.isOutputSupportedFor(android.media.MediaRecorder.class));
1749                 assertTrue("MediaCodec must be supported",
1750                     config.isOutputSupportedFor(android.media.MediaCodec.class));
1751                 assertTrue("Allocation must be supported",
1752                     config.isOutputSupportedFor(android.renderscript.Allocation.class));
1753                 assertTrue("SurfaceHolder must be supported",
1754                     config.isOutputSupportedFor(android.view.SurfaceHolder.class));
1755                 assertTrue("SurfaceTexture must be supported",
1756                     config.isOutputSupportedFor(android.graphics.SurfaceTexture.class));
1757 
1758                 assertTrue("YUV_420_888 must be supported",
1759                     config.isOutputSupportedFor(ImageFormat.YUV_420_888));
1760                 assertTrue("JPEG must be supported",
1761                     config.isOutputSupportedFor(ImageFormat.JPEG));
1762             } else {
1763                 assertTrue("YUV_420_88 may not be supported if BACKWARD_COMPATIBLE capability is not listed",
1764                     !config.isOutputSupportedFor(ImageFormat.YUV_420_888));
1765                 assertTrue("JPEG may not be supported if BACKWARD_COMPATIBLE capability is not listed",
1766                     !config.isOutputSupportedFor(ImageFormat.JPEG));
1767             }
1768 
1769             // Check RAW
1770 
1771             if (arrayContains(actualCapabilities,
1772                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
1773                 assertTrue("RAW_SENSOR must be supported if RAW capability is advertised",
1774                     config.isOutputSupportedFor(ImageFormat.RAW_SENSOR));
1775             }
1776 
1777             // Cross check public formats and sizes
1778 
1779             int[] supportedFormats = config.getOutputFormats();
1780             for (int format : supportedFormats) {
1781                 assertTrue("Format " + format + " fails cross check",
1782                         config.isOutputSupportedFor(format));
1783                 List<Size> supportedSizes = CameraTestUtils.getAscendingOrderSizes(
1784                         Arrays.asList(config.getOutputSizes(format)), /*ascending*/true);
1785                 if (arrayContains(actualCapabilities,
1786                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
1787                     supportedSizes.addAll(
1788                         Arrays.asList(config.getHighResolutionOutputSizes(format)));
1789                     supportedSizes = CameraTestUtils.getAscendingOrderSizes(
1790                         supportedSizes, /*ascending*/true);
1791                 }
1792                 assertTrue("Supported format " + format + " has no sizes listed",
1793                         supportedSizes.size() > 0);
1794                 for (int j = 0; j < supportedSizes.size(); j++) {
1795                     Size size = supportedSizes.get(j);
1796                     if (VERBOSE) {
1797                         Log.v(TAG,
1798                                 String.format("Testing camera %s, format %d, size %s",
1799                                         mAllCameraIds[i], format, size.toString()));
1800                     }
1801 
1802                     long stallDuration = config.getOutputStallDuration(format, size);
1803                     switch(format) {
1804                         case ImageFormat.YUV_420_888:
1805                             assertTrue("YUV_420_888 may not have a non-zero stall duration",
1806                                     stallDuration == 0);
1807                             break;
1808                         case ImageFormat.JPEG:
1809                         case ImageFormat.RAW_SENSOR:
1810                             final float TOLERANCE_FACTOR = 2.0f;
1811                             long prevDuration = 0;
1812                             if (j > 0) {
1813                                 prevDuration = config.getOutputStallDuration(
1814                                         format, supportedSizes.get(j - 1));
1815                             }
1816                             long nextDuration = Long.MAX_VALUE;
1817                             if (j < (supportedSizes.size() - 1)) {
1818                                 nextDuration = config.getOutputStallDuration(
1819                                         format, supportedSizes.get(j + 1));
1820                             }
1821                             long curStallDuration = config.getOutputStallDuration(format, size);
1822                             // Stall duration should be in a reasonable range: larger size should
1823                             // normally have larger stall duration.
1824                             mCollector.expectInRange("Stall duration (format " + format +
1825                                     " and size " + size + ") is not in the right range",
1826                                     curStallDuration,
1827                                     (long) (prevDuration / TOLERANCE_FACTOR),
1828                                     (long) (nextDuration * TOLERANCE_FACTOR));
1829                             break;
1830                         default:
1831                             assertTrue("Negative stall duration for format " + format,
1832                                     stallDuration >= 0);
1833                             break;
1834                     }
1835                     long minDuration = config.getOutputMinFrameDuration(format, size);
1836                     if (arrayContains(actualCapabilities,
1837                             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
1838                         assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
1839                                 + "format " + format + " for size " + size + " minDuration " +
1840                                 minDuration,
1841                                 minDuration > 0);
1842                     } else {
1843                         assertTrue("Need non-negative min frame duration for format " + format,
1844                                 minDuration >= 0);
1845                     }
1846 
1847                     // todo: test opaque image reader when it's supported.
1848                     if (format != ImageFormat.PRIVATE) {
1849                         ImageReader testReader = ImageReader.newInstance(
1850                             size.getWidth(),
1851                             size.getHeight(),
1852                             format,
1853                             1);
1854                         Surface testSurface = testReader.getSurface();
1855 
1856                         assertTrue(
1857                             String.format("isOutputSupportedFor fails for config %s, format %d",
1858                                     size.toString(), format),
1859                             config.isOutputSupportedFor(testSurface));
1860 
1861                         testReader.close();
1862                     }
1863                 } // sizes
1864 
1865                 // Try an invalid size in this format, should round
1866                 Size invalidSize = findInvalidSize(supportedSizes);
1867                 int MAX_ROUNDING_WIDTH = 1920;
1868                 // todo: test opaque image reader when it's supported.
1869                 if (format != ImageFormat.PRIVATE &&
1870                         invalidSize.getWidth() <= MAX_ROUNDING_WIDTH) {
1871                     ImageReader testReader = ImageReader.newInstance(
1872                                                                      invalidSize.getWidth(),
1873                                                                      invalidSize.getHeight(),
1874                                                                      format,
1875                                                                      1);
1876                     Surface testSurface = testReader.getSurface();
1877 
1878                     assertTrue(
1879                                String.format("isOutputSupportedFor fails for config %s, %d",
1880                                        invalidSize.toString(), format),
1881                                config.isOutputSupportedFor(testSurface));
1882 
1883                     testReader.close();
1884                 }
1885             } // formats
1886 
1887             // Cross-check opaque format and sizes
1888             if (arrayContains(actualCapabilities, BC)) {
1889                 SurfaceTexture st = new SurfaceTexture(1);
1890                 Surface surf = new Surface(st);
1891 
1892                 Size[] opaqueSizes = CameraTestUtils.getSupportedSizeForClass(SurfaceTexture.class,
1893                         mAllCameraIds[i], mCameraManager);
1894                 assertTrue("Opaque format has no sizes listed",
1895                         opaqueSizes.length > 0);
1896                 for (Size size : opaqueSizes) {
1897                     long stallDuration = config.getOutputStallDuration(SurfaceTexture.class, size);
1898                     assertTrue("Opaque output may not have a non-zero stall duration",
1899                             stallDuration == 0);
1900 
1901                     long minDuration = config.getOutputMinFrameDuration(SurfaceTexture.class, size);
1902                     if (arrayContains(actualCapabilities,
1903                                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
1904                         assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
1905                                 + "opaque format",
1906                                 minDuration > 0);
1907                     } else {
1908                         assertTrue("Need non-negative min frame duration for opaque format ",
1909                                 minDuration >= 0);
1910                     }
1911                     st.setDefaultBufferSize(size.getWidth(), size.getHeight());
1912 
1913                     assertTrue(
1914                             String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
1915                                     size.toString()),
1916                             config.isOutputSupportedFor(surf));
1917 
1918                 } // opaque sizes
1919 
1920                 // Try invalid opaque size, should get rounded
1921                 Size invalidSize = findInvalidSize(opaqueSizes);
1922                 st.setDefaultBufferSize(invalidSize.getWidth(), invalidSize.getHeight());
1923                 assertTrue(
1924                         String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
1925                                 invalidSize.toString()),
1926                         config.isOutputSupportedFor(surf));
1927 
1928             }
1929         } // mCharacteristics
1930     }
1931 
1932     /**
1933      * Test high speed capability and cross-check the high speed sizes and fps ranges from
1934      * the StreamConfigurationMap.
1935      */
testConstrainedHighSpeedCapability()1936     public void testConstrainedHighSpeedCapability() throws Exception {
1937         for (int i = 0; i < mAllCameraIds.length; i++) {
1938             CameraCharacteristics c = mCharacteristics.get(i);
1939             int[] capabilities = CameraTestUtils.getValueNotNull(
1940                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1941             boolean supportHighSpeed = arrayContains(capabilities, CONSTRAINED_HIGH_SPEED);
1942             if (supportHighSpeed) {
1943                 StreamConfigurationMap config =
1944                         CameraTestUtils.getValueNotNull(
1945                                 c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1946                 List<Size> highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes());
1947                 assertTrue("High speed sizes shouldn't be empty", highSpeedSizes.size() > 0);
1948                 Size[] allSizes = CameraTestUtils.getSupportedSizeForFormat(ImageFormat.PRIVATE,
1949                         mAllCameraIds[i], mCameraManager);
1950                 assertTrue("Normal size for PRIVATE format shouldn't be null or empty",
1951                         allSizes != null && allSizes.length > 0);
1952                 for (Size size: highSpeedSizes) {
1953                     // The sizes must be a subset of the normal sizes
1954                     assertTrue("High speed size " + size +
1955                             " must be part of normal sizes " + Arrays.toString(allSizes),
1956                             Arrays.asList(allSizes).contains(size));
1957 
1958                     // Sanitize the high speed FPS ranges for each size
1959                     List<Range<Integer>> ranges =
1960                             Arrays.asList(config.getHighSpeedVideoFpsRangesFor(size));
1961                     for (Range<Integer> range : ranges) {
1962                         assertTrue("The range " + range + " doesn't satisfy the"
1963                                 + " min/max boundary requirements.",
1964                                 range.getLower() >= HIGH_SPEED_FPS_LOWER_MIN &&
1965                                 range.getUpper() >= HIGH_SPEED_FPS_UPPER_MIN);
1966                         assertTrue("The range " + range + " should be multiple of 30fps",
1967                                 range.getLower() % 30 == 0 && range.getUpper() % 30 == 0);
1968                         // If the range is fixed high speed range, it should contain the
1969                         // [30, fps_max] in the high speed range list; if it's variable FPS range,
1970                         // the corresponding fixed FPS Range must be included in the range list.
1971                         if (range.getLower() == range.getUpper()) {
1972                             Range<Integer> variableRange = new Range<Integer>(30, range.getUpper());
1973                             assertTrue("The variable FPS range " + variableRange +
1974                                     " shoould be included in the high speed ranges for size " +
1975                                     size, ranges.contains(variableRange));
1976                         } else {
1977                             Range<Integer> fixedRange =
1978                                     new Range<Integer>(range.getUpper(), range.getUpper());
1979                             assertTrue("The fixed FPS range " + fixedRange +
1980                                     " shoould be included in the high speed ranges for size " +
1981                                     size, ranges.contains(fixedRange));
1982                         }
1983                     }
1984                 }
1985                 // If the device advertise some high speed profiles, the sizes and FPS ranges
1986                 // should be advertise by the camera.
1987                 for (int quality = CamcorderProfile.QUALITY_HIGH_SPEED_480P;
1988                         quality <= CamcorderProfile.QUALITY_HIGH_SPEED_2160P; quality++) {
1989                     int cameraId = Integer.valueOf(mAllCameraIds[i]);
1990                     if (CamcorderProfile.hasProfile(cameraId, quality)) {
1991                         CamcorderProfile profile = CamcorderProfile.get(cameraId, quality);
1992                         Size camcorderProfileSize =
1993                                 new Size(profile.videoFrameWidth, profile.videoFrameHeight);
1994                         assertTrue("CamcorderPrfile size " + camcorderProfileSize +
1995                                 " must be included in the high speed sizes " +
1996                                 Arrays.toString(highSpeedSizes.toArray()),
1997                                 highSpeedSizes.contains(camcorderProfileSize));
1998                         Range<Integer> camcorderFpsRange =
1999                                 new Range<Integer>(profile.videoFrameRate, profile.videoFrameRate);
2000                         List<Range<Integer>> allRanges =
2001                                 Arrays.asList(config.getHighSpeedVideoFpsRangesFor(
2002                                         camcorderProfileSize));
2003                         assertTrue("Camcorder fps range " + camcorderFpsRange +
2004                                 " should be included by high speed fps ranges " +
2005                                 Arrays.toString(allRanges.toArray()),
2006                                 allRanges.contains(camcorderFpsRange));
2007                     }
2008                 }
2009             }
2010         }
2011     }
2012 
2013     /**
2014      * Correctness check of optical black regions.
2015      */
testOpticalBlackRegions()2016     public void testOpticalBlackRegions() {
2017         for (int i = 0; i < mAllCameraIds.length; i++) {
2018             CameraCharacteristics c = mCharacteristics.get(i);
2019             List<CaptureResult.Key<?>> resultKeys = c.getAvailableCaptureResultKeys();
2020             boolean hasDynamicBlackLevel =
2021                     resultKeys.contains(CaptureResult.SENSOR_DYNAMIC_BLACK_LEVEL);
2022             boolean hasDynamicWhiteLevel =
2023                     resultKeys.contains(CaptureResult.SENSOR_DYNAMIC_WHITE_LEVEL);
2024             boolean hasFixedBlackLevel =
2025                     c.getKeys().contains(CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN);
2026             boolean hasFixedWhiteLevel =
2027                     c.getKeys().contains(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
2028             // The black and white levels should be either all supported or none of them is
2029             // supported.
2030             mCollector.expectTrue("Dynamic black and white level should be all or none of them"
2031                     + " be supported", hasDynamicWhiteLevel == hasDynamicBlackLevel);
2032             mCollector.expectTrue("Fixed black and white level should be all or none of them"
2033                     + " be supported", hasFixedBlackLevel == hasFixedWhiteLevel);
2034             mCollector.expectTrue("Fixed black level should be supported if dynamic black"
2035                     + " level is supported", !hasDynamicBlackLevel || hasFixedBlackLevel);
2036 
2037             if (c.getKeys().contains(CameraCharacteristics.SENSOR_OPTICAL_BLACK_REGIONS)) {
2038                 // Regions shouldn't be null or empty.
2039                 Rect[] regions = CameraTestUtils.getValueNotNull(c,
2040                         CameraCharacteristics.SENSOR_OPTICAL_BLACK_REGIONS);
2041                 CameraTestUtils.assertArrayNotEmpty(regions, "Optical back region arrays must not"
2042                         + " be empty");
2043 
2044                 // Dynamic black level should be supported if the optical black region is
2045                 // advertised.
2046                 mCollector.expectTrue("Dynamic black and white level keys should be advertised in "
2047                         + "available capture result key list", hasDynamicWhiteLevel);
2048 
2049                 // Range check.
2050                 for (Rect region : regions) {
2051                     mCollector.expectTrue("Camera " + mAllCameraIds[i] + ": optical black region" +
2052                             " shouldn't be empty!", !region.isEmpty());
2053                     mCollector.expectGreaterOrEqual("Optical black region left", 0/*expected*/,
2054                             region.left/*actual*/);
2055                     mCollector.expectGreaterOrEqual("Optical black region top", 0/*expected*/,
2056                             region.top/*actual*/);
2057                     mCollector.expectTrue("Optical black region left/right/width/height must be"
2058                             + " even number, otherwise, the bayer CFA pattern in this region will"
2059                             + " be messed up",
2060                             region.left % 2 == 0 && region.top % 2 == 0 &&
2061                             region.width() % 2 == 0 && region.height() % 2 == 0);
2062                     mCollector.expectGreaterOrEqual("Optical black region top", 0/*expected*/,
2063                             region.top/*actual*/);
2064                     Size size = CameraTestUtils.getValueNotNull(c,
2065                             CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
2066                     mCollector.expectLessOrEqual("Optical black region width",
2067                             size.getWidth()/*expected*/, region.width());
2068                     mCollector.expectLessOrEqual("Optical black region height",
2069                             size.getHeight()/*expected*/, region.height());
2070                     Rect activeArray = CameraTestUtils.getValueNotNull(c,
2071                             CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
2072                     mCollector.expectTrue("Optical black region" + region + " should be outside of"
2073                             + " active array " + activeArray,
2074                             !region.intersect(activeArray));
2075                     // Region need to be disjoint:
2076                     for (Rect region2 : regions) {
2077                         mCollector.expectTrue("Optical black region" + region + " should have no "
2078                                 + "overlap with " + region2,
2079                                 region == region2 || !region.intersect(region2));
2080                     }
2081                 }
2082             } else {
2083                 Log.i(TAG, "Camera " + mAllCameraIds[i] + " doesn't support optical black regions,"
2084                         + " skip the region test");
2085             }
2086         }
2087     }
2088 
2089     /**
2090      * Check Logical camera capability
2091      */
testLogicalCameraCharacteristics()2092     public void testLogicalCameraCharacteristics() throws Exception {
2093         for (int i = 0; i < mAllCameraIds.length; i++) {
2094             CameraCharacteristics c = mCharacteristics.get(i);
2095             int[] capabilities = CameraTestUtils.getValueNotNull(
2096                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
2097             boolean supportLogicalCamera = arrayContains(capabilities,
2098                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA);
2099             if (supportLogicalCamera) {
2100                 Set<String> physicalCameraIds = c.getPhysicalCameraIds();
2101                 assertNotNull("android.logicalCam.physicalCameraIds shouldn't be null",
2102                     physicalCameraIds);
2103                 assertTrue("Logical camera must contain at least 2 physical camera ids",
2104                     physicalCameraIds.size() >= 2);
2105 
2106                 mCollector.expectKeyValueInRange(c,
2107                         CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE,
2108                         CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE,
2109                         CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED);
2110 
2111                 Integer timestampSource = c.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
2112                 for (String physicalCameraId : physicalCameraIds) {
2113                     assertNotNull("Physical camera id shouldn't be null", physicalCameraId);
2114                     assertTrue(
2115                             String.format("Physical camera id %s shouldn't be the same as logical"
2116                                     + " camera id %s", physicalCameraId, mAllCameraIds[i]),
2117                             physicalCameraId != mAllCameraIds[i]);
2118 
2119                     //validation for depth static metadata of physical cameras
2120                     CameraCharacteristics pc =
2121                             mCameraManager.getCameraCharacteristics(physicalCameraId);
2122 
2123                     float[] poseRotation = pc.get(CameraCharacteristics.LENS_POSE_ROTATION);
2124                     float[] poseTranslation = pc.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
2125                     Integer poseReference = pc.get(CameraCharacteristics.LENS_POSE_REFERENCE);
2126                     float[] cameraIntrinsics = pc.get(
2127                             CameraCharacteristics.LENS_INTRINSIC_CALIBRATION);
2128                     float[] distortion = getLensDistortion(pc);
2129                     Rect precorrectionArray = pc.get(
2130                             CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
2131 
2132                     verifyLensCalibration(poseRotation, poseTranslation, poseReference,
2133                             cameraIntrinsics, distortion, precorrectionArray);
2134 
2135                     Integer timestampSourcePhysical =
2136                             pc.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
2137                     mCollector.expectEquals("Logical camera and physical cameras must have same " +
2138                             "timestamp source", timestampSource, timestampSourcePhysical);
2139                 }
2140             }
2141 
2142             // Verify that if multiple focal lengths or apertures are supported, they are in
2143             // ascending order.
2144             Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
2145             boolean isExternalCamera = (hwLevel ==
2146                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
2147             if (!isExternalCamera) {
2148                 float[] focalLengths = c.get(
2149                         CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
2150                 for (int j = 0; j < focalLengths.length-1; j++) {
2151                     mCollector.expectTrue("Camera's available focal lengths must be ascending!",
2152                             focalLengths[j] < focalLengths[j+1]);
2153                 }
2154                 float[] apertures = c.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES);
2155                 for (int j = 0; j < apertures.length-1; j++) {
2156                     mCollector.expectTrue("Camera's available apertures must be ascending!",
2157                             apertures[j] < apertures[j+1]);
2158                 }
2159             }
2160         }
2161     }
2162 
2163     /**
2164      * Check monochrome camera capability
2165      */
testMonochromeCharacteristics()2166     public void testMonochromeCharacteristics() {
2167         for (int i = 0; i < mAllCameraIds.length; i++) {
2168             Log.i(TAG, "testMonochromeCharacteristics: Testing camera ID " + mAllCameraIds[i]);
2169 
2170             CameraCharacteristics c = mCharacteristics.get(i);
2171             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
2172             assertNotNull("android.request.availableCapabilities must never be null",
2173                     capabilities);
2174             boolean supportMonochrome = arrayContains(capabilities, MONOCHROME);
2175 
2176             if (!supportMonochrome) {
2177                 continue;
2178             }
2179 
2180             List<Key<?>> allKeys = c.getKeys();
2181             List<CaptureRequest.Key<?>> requestKeys = c.getAvailableCaptureRequestKeys();
2182             List<CaptureResult.Key<?>> resultKeys = c.getAvailableCaptureResultKeys();
2183 
2184             assertTrue("Monochrome camera must have BACKWARD_COMPATIBLE capability",
2185                     arrayContains(capabilities, BC));
2186             int colorFilterArrangement = c.get(
2187                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
2188             assertTrue("Monochrome camera must have either MONO or NIR color filter pattern",
2189                     colorFilterArrangement ==
2190                             CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO
2191                     || colorFilterArrangement ==
2192                             CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR);
2193 
2194             assertFalse("Monochrome camera must not contain SENSOR_CALIBRATION_TRANSFORM1 key",
2195                     allKeys.contains(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1));
2196             assertFalse("Monochrome camera must not contain SENSOR_COLOR_TRANSFORM1 key",
2197                     allKeys.contains(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1));
2198             assertFalse("Monochrome camera must not contain SENSOR_FORWARD_MATRIX1 key",
2199                     allKeys.contains(CameraCharacteristics.SENSOR_FORWARD_MATRIX1));
2200             assertFalse("Monochrome camera must not contain SENSOR_REFERENCE_ILLUMINANT1 key",
2201                     allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1));
2202             assertFalse("Monochrome camera must not contain SENSOR_CALIBRATION_TRANSFORM2 key",
2203                     allKeys.contains(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2));
2204             assertFalse("Monochrome camera must not contain SENSOR_COLOR_TRANSFORM2 key",
2205                     allKeys.contains(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2));
2206             assertFalse("Monochrome camera must not contain SENSOR_FORWARD_MATRIX2 key",
2207                     allKeys.contains(CameraCharacteristics.SENSOR_FORWARD_MATRIX2));
2208             assertFalse("Monochrome camera must not contain SENSOR_REFERENCE_ILLUMINANT2 key",
2209                     allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2));
2210 
2211             assertFalse(
2212                     "Monochrome capture result must not contain SENSOR_NEUTRAL_COLOR_POINT key",
2213                     resultKeys.contains(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT));
2214             assertFalse("Monochrome capture result must not contain SENSOR_GREEN_SPLIT key",
2215                     resultKeys.contains(CaptureResult.SENSOR_GREEN_SPLIT));
2216 
2217             // Check that color correction tags are not available for monochrome cameras
2218             assertTrue("Monochrome camera must not have MANUAL_POST_PROCESSING capability",
2219                     !arrayContains(capabilities, MANUAL_POSTPROC));
2220             assertTrue("Monochrome camera must not have COLOR_CORRECTION_MODE in request keys",
2221                     !requestKeys.contains(CaptureRequest.COLOR_CORRECTION_MODE));
2222             assertTrue("Monochrome camera must not have COLOR_CORRECTION_MODE in result keys",
2223                     !resultKeys.contains(CaptureResult.COLOR_CORRECTION_MODE));
2224             assertTrue("Monochrome camera must not have COLOR_CORRECTION_TRANSFORM in request keys",
2225                     !requestKeys.contains(CaptureRequest.COLOR_CORRECTION_TRANSFORM));
2226             assertTrue("Monochrome camera must not have COLOR_CORRECTION_TRANSFORM in result keys",
2227                     !resultKeys.contains(CaptureResult.COLOR_CORRECTION_TRANSFORM));
2228             assertTrue("Monochrome camera must not have COLOR_CORRECTION_GAINS in request keys",
2229                     !requestKeys.contains(CaptureRequest.COLOR_CORRECTION_GAINS));
2230             assertTrue("Monochrome camera must not have COLOR_CORRECTION_GAINS in result keys",
2231                     !resultKeys.contains(CaptureResult.COLOR_CORRECTION_GAINS));
2232 
2233             // Check that awbSupportedModes only contains AUTO
2234             int[] awbAvailableModes = c.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES);
2235             assertTrue("availableAwbModes must not be null", awbAvailableModes != null);
2236             assertTrue("availableAwbModes must contain only AUTO", awbAvailableModes.length == 1 &&
2237                     awbAvailableModes[0] == CaptureRequest.CONTROL_AWB_MODE_AUTO);
2238         }
2239     }
2240 
matchParametersToCharacteritics(Camera.Parameters params, Camera.CameraInfo info, CameraCharacteristics ch)2241     private boolean matchParametersToCharacteritics(Camera.Parameters params,
2242             Camera.CameraInfo info, CameraCharacteristics ch) {
2243         Integer facing = ch.get(CameraCharacteristics.LENS_FACING);
2244         switch (facing.intValue()) {
2245             case CameraMetadata.LENS_FACING_EXTERNAL:
2246             case CameraMetadata.LENS_FACING_FRONT:
2247                 if (info.facing != Camera.CameraInfo.CAMERA_FACING_FRONT) {
2248                     return false;
2249                 }
2250                 break;
2251             case CameraMetadata.LENS_FACING_BACK:
2252                 if (info.facing != Camera.CameraInfo.CAMERA_FACING_BACK) {
2253                     return false;
2254                 }
2255                 break;
2256             default:
2257                 return false;
2258         }
2259 
2260         Integer orientation = ch.get(CameraCharacteristics.SENSOR_ORIENTATION);
2261         if (orientation.intValue() != info.orientation) {
2262             return false;
2263         }
2264 
2265         StaticMetadata staticMeta = new StaticMetadata(ch);
2266         boolean legacyHasFlash = params.getSupportedFlashModes() != null;
2267         if (staticMeta.hasFlash() != legacyHasFlash) {
2268             return false;
2269         }
2270 
2271         List<String> legacyFocusModes = params.getSupportedFocusModes();
2272         boolean legacyHasFocuser = !((legacyFocusModes.size() == 1) &&
2273                 (legacyFocusModes.contains(Camera.Parameters.FOCUS_MODE_FIXED)));
2274         if (staticMeta.hasFocuser() != legacyHasFocuser) {
2275             return false;
2276         }
2277 
2278         if (staticMeta.isVideoStabilizationSupported() != params.isVideoStabilizationSupported()) {
2279             return false;
2280         }
2281 
2282         float legacyFocalLength = params.getFocalLength();
2283         float [] focalLengths = staticMeta.getAvailableFocalLengthsChecked();
2284         boolean found = false;
2285         for (float focalLength : focalLengths) {
2286             if (Math.abs(focalLength - legacyFocalLength) <= FOCAL_LENGTH_TOLERANCE) {
2287                 found = true;
2288                 break;
2289             }
2290         }
2291 
2292         return found;
2293     }
2294 
2295     /**
2296      * Check that all devices available through the legacy API are also
2297      * accessible via Camera2.
2298      */
2299     @CddTest(requirement="7.5.4/C-0-11")
testLegacyCameraDeviceParity()2300     public void testLegacyCameraDeviceParity() {
2301         int legacyDeviceCount = Camera.getNumberOfCameras();
2302         assertTrue("More legacy devices: " + legacyDeviceCount + " compared to Camera2 devices: " +
2303                 mCharacteristics.size(), legacyDeviceCount <= mCharacteristics.size());
2304 
2305         ArrayList<CameraCharacteristics> chars = new ArrayList<> (mCharacteristics);
2306         for (int i = 0; i < legacyDeviceCount; i++) {
2307             Camera camera = null;
2308             Camera.Parameters legacyParams = null;
2309             Camera.CameraInfo legacyInfo = new Camera.CameraInfo();
2310             try {
2311                 Camera.getCameraInfo(i, legacyInfo);
2312                 camera = Camera.open(i);
2313                 legacyParams = camera.getParameters();
2314 
2315                 assertNotNull("Camera parameters for device: " + i + "  must not be null",
2316                         legacyParams);
2317             } finally {
2318                 if (camera != null) {
2319                     camera.release();
2320                 }
2321             }
2322 
2323             // Camera Ids between legacy devices and Camera2 device could be
2324             // different try to match devices by using other common traits.
2325             CameraCharacteristics found = null;
2326             for (CameraCharacteristics ch : chars) {
2327                 if (matchParametersToCharacteritics(legacyParams, legacyInfo, ch)) {
2328                     found = ch;
2329                     break;
2330                 }
2331             }
2332             assertNotNull("No matching Camera2 device for legacy device id: " + i, found);
2333 
2334             chars.remove(found);
2335         }
2336     }
2337 
2338     /**
2339      * Check camera orientation against device orientation
2340      */
2341     @CddTest(requirement="7.5.5/C-1-1")
testCameraOrientationAlignedWithDevice()2342     public void testCameraOrientationAlignedWithDevice() {
2343         WindowManager windowManager =
2344                 (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
2345         Display display = windowManager.getDefaultDisplay();
2346         DisplayMetrics metrics = new DisplayMetrics();
2347         display.getMetrics(metrics);
2348 
2349         // For square screen, test is guaranteed to pass
2350         if (metrics.widthPixels == metrics.heightPixels) {
2351             return;
2352         }
2353 
2354         // Handle display rotation
2355         int displayRotation = display.getRotation();
2356         if (displayRotation == Surface.ROTATION_90 || displayRotation == Surface.ROTATION_270) {
2357             int tmp = metrics.widthPixels;
2358             metrics.widthPixels = metrics.heightPixels;
2359             metrics.heightPixels = tmp;
2360         }
2361         boolean isDevicePortrait = metrics.widthPixels < metrics.heightPixels;
2362 
2363         for (int i = 0; i < mAllCameraIds.length; i++) {
2364             CameraCharacteristics c = mCharacteristics.get(i);
2365             // Camera size
2366             Size pixelArraySize = c.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
2367             // Camera orientation
2368             int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
2369 
2370             // For square sensor, test is guaranteed to pass
2371             if (pixelArraySize.getWidth() == pixelArraySize.getHeight()) {
2372                 continue;
2373             }
2374 
2375             // Camera size adjusted for device native orientation.
2376             Size adjustedSensorSize;
2377             if (sensorOrientation == 90 || sensorOrientation == 270) {
2378                 adjustedSensorSize = new Size(
2379                         pixelArraySize.getHeight(), pixelArraySize.getWidth());
2380             } else {
2381                 adjustedSensorSize = pixelArraySize;
2382             }
2383 
2384             boolean isCameraPortrait =
2385                     adjustedSensorSize.getWidth() < adjustedSensorSize.getHeight();
2386             assertFalse("Camera " + mAllCameraIds[i] + "'s long dimension must "
2387                     + "align with screen's long dimension", isDevicePortrait^isCameraPortrait);
2388         }
2389     }
2390 
2391     /**
2392      * Get lens distortion coefficients, as a list of 6 floats; returns null if no valid
2393      * distortion field is available
2394      */
2395     private float[] getLensDistortion(CameraCharacteristics c) {
2396         float[] distortion = null;
2397         float[] newDistortion = c.get(CameraCharacteristics.LENS_DISTORTION);
2398         if (Build.VERSION.FIRST_SDK_INT > Build.VERSION_CODES.O_MR1 || newDistortion != null) {
2399             // New devices need to use fixed radial distortion definition; old devices can
2400             // opt-in to it
2401             if (newDistortion != null && newDistortion.length == 5) {
2402                 distortion = new float[6];
2403                 distortion[0] = 1.0f;
2404                 for (int i = 1; i < 6; i++) {
2405                     distortion[i] = newDistortion[i-1];
2406                 }
2407             }
2408         } else {
2409             // Select old field only if on older first SDK and new definition not available
2410             distortion = c.get(CameraCharacteristics.LENS_RADIAL_DISTORTION);
2411         }
2412         return distortion;
2413     }
2414 
2415     /**
2416      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
2417      */
2418     private Size findInvalidSize(Size[] goodSizes) {
2419         return findInvalidSize(Arrays.asList(goodSizes));
2420     }
2421 
2422     /**
2423      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
2424      */
2425     private Size findInvalidSize(List<Size> goodSizes) {
2426         Size invalidSize = new Size(goodSizes.get(0).getWidth() + 1, goodSizes.get(0).getHeight());
2427         while(goodSizes.contains(invalidSize)) {
2428             invalidSize = new Size(invalidSize.getWidth() + 1, invalidSize.getHeight());
2429         }
2430         return invalidSize;
2431     }
2432 
2433     /**
2434      * Check key is present in characteristics if the hardware level is at least {@code hwLevel};
2435      * check that the key is present if the actual capabilities are one of {@code capabilities}.
2436      *
2437      * @return value of the {@code key} from {@code c}
2438      */
2439     private <T> T expectKeyAvailable(CameraCharacteristics c, CameraCharacteristics.Key<T> key,
2440             int hwLevel, int... capabilities) {
2441 
2442         Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
2443         assertNotNull("android.info.supportedHardwareLevel must never be null", actualHwLevel);
2444 
2445         int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
2446         assertNotNull("android.request.availableCapabilities must never be null",
2447                 actualCapabilities);
2448 
2449         List<Key<?>> allKeys = c.getKeys();
2450 
2451         T value = c.get(key);
2452 
2453         // For LIMITED-level targeted keys, rely on capability check, not level
2454         if ((compareHardwareLevel(actualHwLevel, hwLevel) >= 0) && (hwLevel != LIMITED)) {
2455             mCollector.expectTrue(
2456                     String.format("Key (%s) must be in characteristics for this hardware level " +
2457                             "(required minimal HW level %s, actual HW level %s)",
2458                             key.getName(), toStringHardwareLevel(hwLevel),
2459                             toStringHardwareLevel(actualHwLevel)),
2460                     value != null);
2461             mCollector.expectTrue(
2462                     String.format("Key (%s) must be in characteristics list of keys for this " +
2463                             "hardware level (required minimal HW level %s, actual HW level %s)",
2464                             key.getName(), toStringHardwareLevel(hwLevel),
2465                             toStringHardwareLevel(actualHwLevel)),
2466                     allKeys.contains(key));
2467         } else if (arrayContainsAnyOf(actualCapabilities, capabilities)) {
2468             if (!(hwLevel == LIMITED && compareHardwareLevel(actualHwLevel, hwLevel) < 0)) {
2469                 // Don't enforce LIMITED-starting keys on LEGACY level, even if cap is defined
2470                 mCollector.expectTrue(
2471                     String.format("Key (%s) must be in characteristics for these capabilities " +
2472                             "(required capabilities %s, actual capabilities %s)",
2473                             key.getName(), Arrays.toString(capabilities),
2474                             Arrays.toString(actualCapabilities)),
2475                     value != null);
2476                 mCollector.expectTrue(
2477                     String.format("Key (%s) must be in characteristics list of keys for " +
2478                             "these capabilities (required capabilities %s, actual capabilities %s)",
2479                             key.getName(), Arrays.toString(capabilities),
2480                             Arrays.toString(actualCapabilities)),
2481                     allKeys.contains(key));
2482             }
2483         } else {
2484             if (actualHwLevel == LEGACY && hwLevel != OPT) {
2485                 if (value != null || allKeys.contains(key)) {
2486                     Log.w(TAG, String.format(
2487                             "Key (%s) is not required for LEGACY devices but still appears",
2488                             key.getName()));
2489                 }
2490             }
2491             // OK: Key may or may not be present.
2492         }
2493         return value;
2494     }
2495 
2496     private static boolean arrayContains(int[] arr, int needle) {
2497         if (arr == null) {
2498             return false;
2499         }
2500 
2501         for (int elem : arr) {
2502             if (elem == needle) {
2503                 return true;
2504             }
2505         }
2506 
2507         return false;
2508     }
2509 
2510     private static <T> boolean arrayContains(T[] arr, T needle) {
2511         if (arr == null) {
2512             return false;
2513         }
2514 
2515         for (T elem : arr) {
2516             if (elem.equals(needle)) {
2517                 return true;
2518             }
2519         }
2520 
2521         return false;
2522     }
2523 
2524     private static boolean arrayContainsAnyOf(int[] arr, int[] needles) {
2525         for (int needle : needles) {
2526             if (arrayContains(arr, needle)) {
2527                 return true;
2528             }
2529         }
2530         return false;
2531     }
2532 
2533     /**
2534      * The key name has a prefix of either "android." or a valid TLD; other prefixes are not valid.
2535      */
2536     private static void assertKeyPrefixValid(String keyName) {
2537         assertStartsWithAndroidOrTLD(
2538                 "All metadata keys must start with 'android.' (built-in keys) " +
2539                 "or valid TLD (vendor-extended keys)", keyName);
2540     }
2541 
2542     private static void assertTrueForKey(String msg, CameraCharacteristics.Key<?> key,
2543             boolean actual) {
2544         assertTrue(msg + " (key = '" + key.getName() + "')", actual);
2545     }
2546 
2547     private static <T> void assertOneOf(String msg, T[] expected, T actual) {
2548         for (int i = 0; i < expected.length; ++i) {
2549             if (Objects.equals(expected[i], actual)) {
2550                 return;
2551             }
2552         }
2553 
2554         fail(String.format("%s: (expected one of %s, actual %s)",
2555                 msg, Arrays.toString(expected), actual));
2556     }
2557 
2558     private static <T> void assertStartsWithAndroidOrTLD(String msg, String keyName) {
2559         String delimiter = ".";
2560         if (keyName.startsWith(PREFIX_ANDROID + delimiter)) {
2561             return;
2562         }
2563         Pattern tldPattern = Pattern.compile(Patterns.TOP_LEVEL_DOMAIN_STR);
2564         Matcher match = tldPattern.matcher(keyName);
2565         if (match.find(0) && (0 == match.start()) && (!match.hitEnd())) {
2566             if (keyName.regionMatches(match.end(), delimiter, 0, delimiter.length())) {
2567                 return;
2568             }
2569         }
2570 
2571         fail(String.format("%s: (expected to start with %s or valid TLD, but value was %s)",
2572                 msg, PREFIX_ANDROID + delimiter, keyName));
2573     }
2574 
2575     /** Return a positive int if left > right, 0 if left==right, negative int if left < right */
2576     private static int compareHardwareLevel(int left, int right) {
2577         return remapHardwareLevel(left) - remapHardwareLevel(right);
2578     }
2579 
2580     /** Remap HW levels worst<->best, 0 = LEGACY, 1 = LIMITED, 2 = FULL, ..., N = LEVEL_N */
2581     private static int remapHardwareLevel(int level) {
2582         switch (level) {
2583             case OPT:
2584                 return Integer.MAX_VALUE;
2585             case LEGACY:
2586                 return 0; // lowest
2587             case EXTERNAL:
2588                 return 1; // second lowest
2589             case LIMITED:
2590                 return 2;
2591             case FULL:
2592                 return 3; // good
2593             case LEVEL_3:
2594                 return 4;
2595             default:
2596                 fail("Unknown HW level: " + level);
2597         }
2598         return -1;
2599     }
2600 
2601     private static String toStringHardwareLevel(int level) {
2602         switch (level) {
2603             case LEGACY:
2604                 return "LEGACY";
2605             case LIMITED:
2606                 return "LIMITED";
2607             case FULL:
2608                 return "FULL";
2609             case EXTERNAL:
2610                 return "EXTERNAL";
2611             default:
2612                 if (level >= LEVEL_3) {
2613                     return String.format("LEVEL_%d", level);
2614                 }
2615         }
2616 
2617         // unknown
2618         Log.w(TAG, "Unknown hardware level " + level);
2619         return Integer.toString(level);
2620     }
2621 }
2622