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