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