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.camera2.CameraCharacteristics;
24 import android.hardware.camera2.CameraCharacteristics.Key;
25 import android.hardware.camera2.CameraManager;
26 import android.hardware.camera2.CaptureRequest;
27 import android.hardware.camera2.CaptureResult;
28 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
29 import android.hardware.camera2.params.BlackLevelPattern;
30 import android.hardware.camera2.params.ColorSpaceTransform;
31 import android.hardware.camera2.params.StreamConfigurationMap;
32 import android.media.CamcorderProfile;
33 import android.media.ImageReader;
34 import android.os.Build;
35 import android.platform.test.annotations.AppModeFull;
36 import android.test.AndroidTestCase;
37 import android.util.Log;
38 import android.util.Rational;
39 import android.util.Range;
40 import android.util.Size;
41 import android.util.Patterns;
42 import android.view.Surface;
43 import android.view.WindowManager;
44 
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.List;
48 import java.util.Objects;
49 import java.util.regex.Matcher;
50 import java.util.regex.Pattern;
51 import java.util.Set;
52 
53 import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
54 
55 /**
56  * Extended tests for static camera characteristics.
57  */
58 @AppModeFull
59 public class ExtendedCameraCharacteristicsTest extends AndroidTestCase {
60     private static final String TAG = "ExChrsTest"; // must be short so next line doesn't throw
61     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
62 
63     private static final String PREFIX_ANDROID = "android";
64 
65     /*
66      * Constants for static RAW metadata.
67      */
68     private static final int MIN_ALLOWABLE_WHITELEVEL = 32; // must have sensor bit depth > 5
69 
70     private CameraManager mCameraManager;
71     private List<CameraCharacteristics> mCharacteristics;
72     private String[] mIds;
73     private CameraErrorCollector mCollector;
74 
75     private static final Size FULLHD = new Size(1920, 1080);
76     private static final Size FULLHD_ALT = new Size(1920, 1088);
77     private static final Size HD = new Size(1280, 720);
78     private static final Size VGA = new Size(640, 480);
79     private static final Size QVGA = new Size(320, 240);
80 
81     private static final long FRAME_DURATION_30FPS_NSEC = (long) 1e9 / 30;
82     /*
83      * HW Levels short hand
84      */
85     private static final int LEGACY = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
86     private static final int LIMITED = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
87     private static final int FULL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
88     private static final int LEVEL_3 = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3;
89     private static final int EXTERNAL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
90     private static final int OPT = Integer.MAX_VALUE;  // For keys that are optional on all hardware levels.
91 
92     /*
93      * Capabilities short hand
94      */
95     private static final int NONE = -1;
96     private static final int BC =
97             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
98     private static final int MANUAL_SENSOR =
99             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR;
100     private static final int MANUAL_POSTPROC =
101             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING;
102     private static final int RAW =
103             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW;
104     private static final int YUV_REPROCESS =
105             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING;
106     private static final int OPAQUE_REPROCESS =
107             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
108     private static final int CONSTRAINED_HIGH_SPEED =
109             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO;
110     private static final int MONOCHROME =
111             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME;
112     private static final int HIGH_SPEED_FPS_LOWER_MIN = 30;
113     private static final int HIGH_SPEED_FPS_UPPER_MIN = 120;
114 
115     @Override
setContext(Context context)116     public void setContext(Context context) {
117         super.setContext(context);
118         mCameraManager = (CameraManager)context.getSystemService(Context.CAMERA_SERVICE);
119         assertNotNull("Can't connect to camera manager", mCameraManager);
120     }
121 
122     @Override
setUp()123     protected void setUp() throws Exception {
124         super.setUp();
125         mIds = mCameraManager.getCameraIdList();
126         mCharacteristics = new ArrayList<>();
127         mCollector = new CameraErrorCollector();
128         for (int i = 0; i < mIds.length; i++) {
129             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(mIds[i]);
130             assertNotNull(String.format("Can't get camera characteristics from: ID %s", mIds[i]),
131                     props);
132             mCharacteristics.add(props);
133         }
134     }
135 
136     @Override
tearDown()137     protected void tearDown() throws Exception {
138         mCharacteristics = null;
139 
140         try {
141             mCollector.verify();
142         } catch (Throwable e) {
143             // When new Exception(e) is used, exception info will be printed twice.
144             throw new Exception(e.getMessage());
145         } finally {
146             super.tearDown();
147         }
148     }
149 
150     /**
151      * Test that the available stream configurations contain a few required formats and sizes.
152      */
testAvailableStreamConfigs()153     public void testAvailableStreamConfigs() throws Exception {
154         int counter = 0;
155         for (CameraCharacteristics c : mCharacteristics) {
156             StreamConfigurationMap config =
157                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
158             assertNotNull(String.format("No stream configuration map found for: ID %s",
159                     mIds[counter]), config);
160             int[] outputFormats = config.getOutputFormats();
161 
162             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
163             assertNotNull("android.request.availableCapabilities must never be null",
164                     actualCapabilities);
165 
166             // Check required formats exist (JPEG, and YUV_420_888).
167             if (!arrayContains(actualCapabilities, BC)) {
168                 Log.i(TAG, "Camera " + mIds[counter] +
169                     ": BACKWARD_COMPATIBLE capability not supported, skipping test");
170                 continue;
171             }
172 
173             assertArrayContains(
174                     String.format("No valid YUV_420_888 preview formats found for: ID %s",
175                             mIds[counter]), outputFormats, ImageFormat.YUV_420_888);
176             assertArrayContains(String.format("No JPEG image format for: ID %s",
177                     mIds[counter]), outputFormats, ImageFormat.JPEG);
178 
179             Size[] yuvSizes = config.getOutputSizes(ImageFormat.YUV_420_888);
180             Size[] jpegSizes = config.getOutputSizes(ImageFormat.JPEG);
181             Size[] privateSizes = config.getOutputSizes(ImageFormat.PRIVATE);
182 
183             CameraTestUtils.assertArrayNotEmpty(yuvSizes,
184                     String.format("No sizes for preview format %x for: ID %s",
185                             ImageFormat.YUV_420_888, mIds[counter]));
186 
187             Rect activeRect = CameraTestUtils.getValueNotNull(
188                     c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
189             Size activeArraySize = new Size(activeRect.width(), activeRect.height());
190             Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
191 
192             if (activeArraySize.getWidth() >= FULLHD.getWidth() &&
193                     activeArraySize.getHeight() >= FULLHD.getHeight()) {
194                 assertArrayContainsAnyOf(String.format(
195                         "Required FULLHD size not found for format %x for: ID %s",
196                         ImageFormat.JPEG, mIds[counter]), jpegSizes,
197                         new Size[] {FULLHD, FULLHD_ALT});
198             }
199 
200             if (activeArraySize.getWidth() >= HD.getWidth() &&
201                     activeArraySize.getHeight() >= HD.getHeight()) {
202                 assertArrayContains(String.format(
203                         "Required HD size not found for format %x for: ID %s",
204                         ImageFormat.JPEG, mIds[counter]), jpegSizes, HD);
205             }
206 
207             if (activeArraySize.getWidth() >= VGA.getWidth() &&
208                     activeArraySize.getHeight() >= VGA.getHeight()) {
209                 assertArrayContains(String.format(
210                         "Required VGA size not found for format %x for: ID %s",
211                         ImageFormat.JPEG, mIds[counter]), jpegSizes, VGA);
212             }
213 
214             if (activeArraySize.getWidth() >= QVGA.getWidth() &&
215                     activeArraySize.getHeight() >= QVGA.getHeight()) {
216                 assertArrayContains(String.format(
217                         "Required QVGA size not found for format %x for: ID %s",
218                         ImageFormat.JPEG, mIds[counter]), jpegSizes, QVGA);
219             }
220 
221             ArrayList<Size> jpegSizesList = new ArrayList<>(Arrays.asList(jpegSizes));
222             ArrayList<Size> yuvSizesList = new ArrayList<>(Arrays.asList(yuvSizes));
223             ArrayList<Size> privateSizesList = new ArrayList<>(Arrays.asList(privateSizes));
224             boolean isExternalCamera = (hwLevel ==
225                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
226             Size maxVideoSize = null;
227             if (isExternalCamera) {
228                 // TODO: for now, use FULLHD 30 as largest possible video size
229                 List<Size> videoSizes = CameraTestUtils.getSupportedVideoSizes(
230                         mIds[counter], mCameraManager, FULLHD);
231                 for (Size sz : videoSizes) {
232                     long minFrameDuration = config.getOutputMinFrameDuration(
233                             android.media.MediaRecorder.class, sz);
234                     // Give some margin for rounding error
235                     if (minFrameDuration > (1e9 / 30.1)) {
236                         maxVideoSize = sz;
237                         break;
238                     }
239                 }
240             } else {
241                 int cameraId = Integer.valueOf(mIds[counter]);
242                 CamcorderProfile maxVideoProfile = CamcorderProfile.get(
243                         cameraId, CamcorderProfile.QUALITY_HIGH);
244                 maxVideoSize = new Size(
245                         maxVideoProfile.videoFrameWidth, maxVideoProfile.videoFrameHeight);
246             }
247             if (maxVideoSize == null) {
248                 fail("Camera " + mIds[counter] + " does not support any 30fps video output");
249             }
250 
251             // Handle FullHD special case first
252             if (jpegSizesList.contains(FULLHD)) {
253                 if (hwLevel >= LEVEL_3 || hwLevel == FULL || (hwLevel == LIMITED &&
254                         maxVideoSize.getWidth() >= FULLHD.getWidth() &&
255                         maxVideoSize.getHeight() >= FULLHD.getHeight())) {
256                     boolean yuvSupportFullHD = yuvSizesList.contains(FULLHD) ||
257                             yuvSizesList.contains(FULLHD_ALT);
258                     boolean privateSupportFullHD = privateSizesList.contains(FULLHD) ||
259                             privateSizesList.contains(FULLHD_ALT);
260                     assertTrue("Full device FullHD YUV size not found", yuvSupportFullHD);
261                     assertTrue("Full device FullHD PRIVATE size not found", privateSupportFullHD);
262                 }
263                 // remove all FullHD or FullHD_Alt sizes for the remaining of the test
264                 jpegSizesList.remove(FULLHD);
265                 jpegSizesList.remove(FULLHD_ALT);
266             }
267 
268             // Check all sizes other than FullHD
269             if (hwLevel == LIMITED) {
270                 // Remove all jpeg sizes larger than max video size
271                 ArrayList<Size> toBeRemoved = new ArrayList<>();
272                 for (Size size : jpegSizesList) {
273                     if (size.getWidth() >= maxVideoSize.getWidth() &&
274                             size.getHeight() >= maxVideoSize.getHeight()) {
275                         toBeRemoved.add(size);
276                     }
277                 }
278                 jpegSizesList.removeAll(toBeRemoved);
279             }
280 
281             if (hwLevel >= LEVEL_3 || hwLevel == FULL || hwLevel == LIMITED) {
282                 if (!yuvSizesList.containsAll(jpegSizesList)) {
283                     for (Size s : jpegSizesList) {
284                         if (!yuvSizesList.contains(s)) {
285                             fail("Size " + s + " not found in YUV format");
286                         }
287                     }
288                 }
289             }
290 
291             if (!privateSizesList.containsAll(yuvSizesList)) {
292                 for (Size s : yuvSizesList) {
293                     if (!privateSizesList.contains(s)) {
294                         fail("Size " + s + " not found in PRIVATE format");
295                     }
296                 }
297             }
298 
299             counter++;
300         }
301     }
302 
303     /**
304      * Test {@link CameraCharacteristics#getKeys}
305      */
testKeys()306     public void testKeys() {
307         int counter = 0;
308         for (CameraCharacteristics c : mCharacteristics) {
309             mCollector.setCameraId(mIds[counter]);
310 
311             if (VERBOSE) {
312                 Log.v(TAG, "testKeys - testing characteristics for camera " + mIds[counter]);
313             }
314 
315             List<CameraCharacteristics.Key<?>> allKeys = c.getKeys();
316             assertNotNull("Camera characteristics keys must not be null", allKeys);
317             assertFalse("Camera characteristics keys must have at least 1 key",
318                     allKeys.isEmpty());
319 
320             for (CameraCharacteristics.Key<?> key : allKeys) {
321                 assertKeyPrefixValid(key.getName());
322 
323                 // All characteristics keys listed must never be null
324                 mCollector.expectKeyValueNotNull(c, key);
325 
326                 // TODO: add a check that key must not be @hide
327             }
328 
329             /*
330              * List of keys that must be present in camera characteristics (not null).
331              *
332              * Keys for LIMITED, FULL devices might be available despite lacking either
333              * the hardware level or the capability. This is *OK*. This only lists the
334              * *minimal* requirements for a key to be listed.
335              *
336              * LEGACY devices are a bit special since they map to api1 devices, so we know
337              * for a fact most keys are going to be illegal there so they should never be
338              * available.
339              *
340              * For LIMITED-level keys, if the level is >= LIMITED, then the capabilities are used to
341              * do the actual checking.
342              */
343             {
344                 //                                           (Key Name)                                     (HW Level)  (Capabilities <Var-Arg>)
345                 expectKeyAvailable(c, CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES     , OPT      ,   BC                   );
346                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_MODES                         , OPT      ,   BC                   );
347                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES          , OPT      ,   BC                   );
348                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES                      , OPT      ,   BC                   );
349                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES          , OPT      ,   BC                   );
350                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE                   , OPT      ,   BC                   );
351                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP                    , OPT      ,   BC                   );
352                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE                       , OPT      ,   BC                   );
353                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES                      , OPT      ,   BC                   );
354                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS                       , OPT      ,   BC                   );
355                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES                   , OPT      ,   BC                   );
356                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES     , OPT      ,   BC                   );
357                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES                     , OPT      ,   BC                   );
358                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE                      , OPT      ,   BC                   );
359                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AE                          , OPT      ,   BC                   );
360                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AF                          , OPT      ,   BC                   );
361                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB                         , OPT      ,   BC                   );
362                 expectKeyAvailable(c, CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES                       , FULL     ,   NONE                 );
363                 expectKeyAvailable(c, CameraCharacteristics.FLASH_INFO_AVAILABLE                            , OPT      ,   BC                   );
364                 expectKeyAvailable(c, CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES             , OPT      ,   RAW                  );
365                 expectKeyAvailable(c, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL                   , OPT      ,   BC                   );
366                 expectKeyAvailable(c, CameraCharacteristics.INFO_VERSION                                    , OPT      ,   NONE                 );
367                 expectKeyAvailable(c, CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES                  , OPT      ,   BC                   );
368                 expectKeyAvailable(c, CameraCharacteristics.LENS_FACING                                     , OPT      ,   BC                   );
369                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES                   , FULL     ,   MANUAL_SENSOR        );
370                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES            , FULL     ,   MANUAL_SENSOR        );
371                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION       , LIMITED  ,   BC                   );
372                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION            , LIMITED  ,   MANUAL_SENSOR        );
373                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE                   , LIMITED  ,   BC                   );
374                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE                , LIMITED  ,   BC                   );
375                 expectKeyAvailable(c, CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES , OPT      ,   BC                   );
376                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES                  , OPT      ,   BC                   );
377                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS                   , OPT      ,   YUV_REPROCESS, OPAQUE_REPROCESS);
378                 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP                 , OPT      ,   CONSTRAINED_HIGH_SPEED);
379                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC                     , OPT      ,   BC                   );
380                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING            , OPT      ,   BC                   );
381                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW                      , OPT      ,   BC                   );
382                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT                    , OPT      ,   BC                   );
383                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH                      , OPT      ,   BC                   );
384                 expectKeyAvailable(c, CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM               , OPT      ,   BC                   );
385                 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP                 , OPT      ,   BC                   );
386                 expectKeyAvailable(c, CameraCharacteristics.SCALER_CROPPING_TYPE                            , OPT      ,   BC                   );
387                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN                      , FULL     ,   RAW                  );
388                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1                   , OPT      ,   RAW                  );
389                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM1                         , OPT      ,   RAW                  );
390                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX1                          , OPT      ,   RAW                  );
391                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE                   , OPT      ,   BC, RAW              );
392                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT            , FULL     ,   RAW                  );
393                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE                 , FULL     ,   MANUAL_SENSOR        );
394                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION                  , FULL     ,   MANUAL_SENSOR        );
395                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE                    , OPT      ,   BC                   );
396                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE                   , FULL     ,   MANUAL_SENSOR        );
397                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL                         , OPT      ,   RAW                  );
398                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE                    , OPT      ,   BC                   );
399                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY                   , FULL     ,   MANUAL_SENSOR        );
400                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_ORIENTATION                              , OPT      ,   BC                   );
401                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1                    , OPT      ,   RAW                  );
402                 expectKeyAvailable(c, CameraCharacteristics.SHADING_AVAILABLE_MODES                         , LIMITED  ,   MANUAL_POSTPROC, RAW );
403                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES     , OPT      ,   BC                   );
404                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES   , OPT      ,   RAW                  );
405                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES, LIMITED  ,   RAW                  );
406                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT                  , OPT      ,   BC                   );
407                 expectKeyAvailable(c, CameraCharacteristics.SYNC_MAX_LATENCY                                , OPT      ,   BC                   );
408                 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES                , FULL     ,   MANUAL_POSTPROC      );
409                 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS                        , FULL     ,   MANUAL_POSTPROC      );
410 
411                 // Future: Use column editors for modifying above, ignore line length to keep 1 key per line
412 
413                 // TODO: check that no other 'android' keys are listed in #getKeys if they aren't in the above list
414             }
415 
416             // Only check for these if the second reference illuminant is included
417             if (allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2)) {
418                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2                    , OPT      ,   RAW                  );
419                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM2                         , OPT      ,   RAW                  );
420                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2                   , OPT      ,   RAW                  );
421                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX2                          , OPT      ,   RAW                  );
422             }
423 
424             // Required key if any of RAW format output is supported
425             StreamConfigurationMap config =
426                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
427             assertNotNull(String.format("No stream configuration map found for: ID %s",
428                     mIds[counter]), config);
429             if (config.isOutputSupportedFor(ImageFormat.RAW_SENSOR) ||
430                     config.isOutputSupportedFor(ImageFormat.RAW10)  ||
431                     config.isOutputSupportedFor(ImageFormat.RAW12)  ||
432                     config.isOutputSupportedFor(ImageFormat.RAW_PRIVATE)) {
433                 expectKeyAvailable(c,
434                         CameraCharacteristics.CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE, OPT, BC);
435             }
436 
437             // External Camera exceptional keys
438             Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
439             boolean isExternalCamera = (hwLevel ==
440                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
441             if (!isExternalCamera) {
442                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS               , OPT      ,   BC                   );
443                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES             , OPT      ,   BC                   );
444                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE                       , OPT      ,   BC                   );
445             }
446 
447 
448             // Verify version is a short text string.
449             if (allKeys.contains(CameraCharacteristics.INFO_VERSION)) {
450                 final String TEXT_REGEX = "[\\p{Alnum}\\p{Punct}\\p{Space}]*";
451                 final int MAX_VERSION_LENGTH = 256;
452 
453                 String version = c.get(CameraCharacteristics.INFO_VERSION);
454                 mCollector.expectTrue("Version contains non-text characters: " + version,
455                         version.matches(TEXT_REGEX));
456                 mCollector.expectLessOrEqual("Version too long: " + version, MAX_VERSION_LENGTH,
457                         version.length());
458             }
459 
460             counter++;
461         }
462     }
463 
464     /**
465      * Test values for static metadata used by the RAW capability.
466      */
testStaticRawCharacteristics()467     public void testStaticRawCharacteristics() {
468         int counter = 0;
469         for (CameraCharacteristics c : mCharacteristics) {
470             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
471             assertNotNull("android.request.availableCapabilities must never be null",
472                     actualCapabilities);
473             if (!arrayContains(actualCapabilities,
474                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
475                 Log.i(TAG, "RAW capability is not supported in camera " + counter++ +
476                         ". Skip the test.");
477                 continue;
478             }
479 
480             Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
481             if (actualHwLevel != null && actualHwLevel == FULL) {
482                 mCollector.expectKeyValueContains(c,
483                         CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES,
484                         CameraCharacteristics.HOT_PIXEL_MODE_FAST);
485             }
486             mCollector.expectKeyValueContains(c,
487                     CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES, false);
488             mCollector.expectKeyValueGreaterThan(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL,
489                     MIN_ALLOWABLE_WHITELEVEL);
490 
491             mCollector.expectKeyValueIsIn(c,
492                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
493                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB,
494                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG,
495                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG,
496                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR);
497             // TODO: SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB isn't supported yet.
498 
499             mCollector.expectKeyValueInRange(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1,
500                     CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT,
501                     CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN);
502             // Only check the range if the second reference illuminant is avaliable
503             if (c.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2) != null) {
504                 mCollector.expectKeyValueInRange(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2,
505                         (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT,
506                         (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN);
507             }
508 
509             Rational[] zeroes = new Rational[9];
510             Arrays.fill(zeroes, Rational.ZERO);
511 
512             ColorSpaceTransform zeroed = new ColorSpaceTransform(zeroes);
513             mCollector.expectNotEquals("Forward Matrix1 should not contain all zeroes.", zeroed,
514                     c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX1));
515             mCollector.expectNotEquals("Forward Matrix2 should not contain all zeroes.", zeroed,
516                     c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX2));
517             mCollector.expectNotEquals("Calibration Transform1 should not contain all zeroes.",
518                     zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1));
519             mCollector.expectNotEquals("Calibration Transform2 should not contain all zeroes.",
520                     zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2));
521             mCollector.expectNotEquals("Color Transform1 should not contain all zeroes.",
522                     zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1));
523             mCollector.expectNotEquals("Color Transform2 should not contain all zeroes.",
524                     zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2));
525 
526             BlackLevelPattern blackLevel = mCollector.expectKeyValueNotNull(c,
527                     CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN);
528             if (blackLevel != null) {
529                 String blackLevelPatternString = blackLevel.toString();
530                 if (VERBOSE) {
531                     Log.v(TAG, "Black level pattern: " + blackLevelPatternString);
532                 }
533                 int[] blackLevelPattern = new int[BlackLevelPattern.COUNT];
534                 blackLevel.copyTo(blackLevelPattern, /*offset*/0);
535                 Integer whitelevel = c.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
536                 if (whitelevel != null) {
537                     mCollector.expectValuesInRange("BlackLevelPattern", blackLevelPattern, 0,
538                             whitelevel);
539                 } else {
540                     mCollector.addMessage(
541                             "No WhiteLevel available, cannot check BlackLevelPattern range.");
542                 }
543             }
544 
545             // TODO: profileHueSatMap, and profileToneCurve aren't supported yet.
546             counter++;
547         }
548     }
549 
550     /**
551      * Test values for the available session keys.
552      */
testStaticSessionKeys()553     public void testStaticSessionKeys() throws Exception {
554         for (CameraCharacteristics c : mCharacteristics) {
555             List<CaptureRequest.Key<?>> availableSessionKeys = c.getAvailableSessionKeys();
556             if (availableSessionKeys == null) {
557                 continue;
558             }
559             List<CaptureRequest.Key<?>> availableRequestKeys = c.getAvailableCaptureRequestKeys();
560 
561             //Every session key should be part of the available request keys
562             for (CaptureRequest.Key<?> key : availableSessionKeys) {
563                 assertTrue("Session key:" + key.getName() + " not present in the available capture "
564                         + "request keys!", availableRequestKeys.contains(key));
565             }
566         }
567     }
568 
569     /**
570      * Test values for static metadata used by the BURST capability.
571      */
testStaticBurstCharacteristics()572     public void testStaticBurstCharacteristics() throws Exception {
573         int counter = 0;
574         final float SIZE_ERROR_MARGIN = 0.03f;
575         for (CameraCharacteristics c : mCharacteristics) {
576             int[] actualCapabilities = CameraTestUtils.getValueNotNull(
577                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
578 
579             // Check if the burst capability is defined
580             boolean haveBurstCapability = arrayContains(actualCapabilities,
581                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
582             boolean haveBC = arrayContains(actualCapabilities,
583                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
584 
585             if(haveBurstCapability && !haveBC) {
586                 fail("Must have BACKWARD_COMPATIBLE capability if BURST_CAPTURE capability is defined");
587             }
588 
589             if (!haveBC) continue;
590 
591             StreamConfigurationMap config =
592                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
593             assertNotNull(String.format("No stream configuration map found for: ID %s",
594                     mIds[counter]), config);
595             Rect activeRect = CameraTestUtils.getValueNotNull(
596                     c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
597             Size sensorSize = new Size(activeRect.width(), activeRect.height());
598 
599             // Ensure that max YUV size matches max JPEG size
600             Size maxYuvSize = CameraTestUtils.getMaxSize(
601                     config.getOutputSizes(ImageFormat.YUV_420_888));
602             Size maxFastYuvSize = maxYuvSize;
603 
604             Size[] slowYuvSizes = config.getHighResolutionOutputSizes(ImageFormat.YUV_420_888);
605             if (haveBurstCapability && slowYuvSizes != null && slowYuvSizes.length > 0) {
606                 Size maxSlowYuvSize = CameraTestUtils.getMaxSize(slowYuvSizes);
607                 maxYuvSize = CameraTestUtils.getMaxSize(new Size[]{maxYuvSize, maxSlowYuvSize});
608             }
609 
610             Size maxJpegSize = CameraTestUtils.getMaxSize(CameraTestUtils.getSupportedSizeForFormat(
611                     ImageFormat.JPEG, mIds[counter], mCameraManager));
612 
613             boolean haveMaxYuv = maxYuvSize != null ?
614                 (maxJpegSize.getWidth() <= maxYuvSize.getWidth() &&
615                         maxJpegSize.getHeight() <= maxYuvSize.getHeight()) : false;
616 
617             float croppedWidth = (float)sensorSize.getWidth();
618             float croppedHeight = (float)sensorSize.getHeight();
619             float sensorAspectRatio = (float)sensorSize.getWidth() / (float)sensorSize.getHeight();
620             float maxYuvAspectRatio = (float)maxYuvSize.getWidth() / (float)maxYuvSize.getHeight();
621             if (sensorAspectRatio < maxYuvAspectRatio) {
622                 croppedHeight = (float)sensorSize.getWidth() / maxYuvAspectRatio;
623             } else if (sensorAspectRatio > maxYuvAspectRatio) {
624                 croppedWidth = (float)sensorSize.getHeight() * maxYuvAspectRatio;
625             }
626             Size croppedSensorSize = new Size((int)croppedWidth, (int)croppedHeight);
627 
628             boolean maxYuvMatchSensor =
629                     (maxYuvSize.getWidth() <= croppedSensorSize.getWidth() * (1.0 + SIZE_ERROR_MARGIN) &&
630                      maxYuvSize.getWidth() >= croppedSensorSize.getWidth() * (1.0 - SIZE_ERROR_MARGIN) &&
631                      maxYuvSize.getHeight() <= croppedSensorSize.getHeight() * (1.0 + SIZE_ERROR_MARGIN) &&
632                      maxYuvSize.getHeight() >= croppedSensorSize.getHeight() * (1.0 - SIZE_ERROR_MARGIN));
633 
634             // No need to do null check since framework will generate the key if HAL don't supply
635             boolean haveAeLock = CameraTestUtils.getValueNotNull(
636                     c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE);
637             boolean haveAwbLock = CameraTestUtils.getValueNotNull(
638                     c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE);
639 
640             // Ensure that max YUV output is fast enough - needs to be at least 10 fps
641 
642             long maxYuvRate =
643                 config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxYuvSize);
644             final long MIN_MAXSIZE_DURATION_BOUND_NS = 100000000; // 100 ms, 10 fps
645             boolean haveMaxYuvRate = maxYuvRate <= MIN_MAXSIZE_DURATION_BOUND_NS;
646 
647             // Ensure that some >=8MP YUV output is fast enough - needs to be at least 20 fps
648 
649             long maxFastYuvRate =
650                     config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxFastYuvSize);
651             final long MIN_8MP_DURATION_BOUND_NS = 50000000; // 50 ms, 20 fps
652             boolean haveFastYuvRate = maxFastYuvRate <= MIN_8MP_DURATION_BOUND_NS;
653 
654             final int SIZE_8MP_BOUND = 8000000;
655             boolean havefast8MPYuv = (maxFastYuvSize.getWidth() * maxFastYuvSize.getHeight()) >
656                     SIZE_8MP_BOUND;
657 
658             // Ensure that there's an FPS range that's fast enough to capture at above
659             // minFrameDuration, for full-auto bursts at the fast resolutions
660             Range[] fpsRanges = CameraTestUtils.getValueNotNull(
661                     c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
662             float minYuvFps = 1.f / maxFastYuvRate;
663 
664             boolean haveFastAeTargetFps = false;
665             for (Range<Integer> r : fpsRanges) {
666                 if (r.getLower() >= minYuvFps) {
667                     haveFastAeTargetFps = true;
668                     break;
669                 }
670             }
671 
672             // Ensure that maximum sync latency is small enough for fast setting changes, even if
673             // it's not quite per-frame
674 
675             Integer maxSyncLatencyValue = c.get(CameraCharacteristics.SYNC_MAX_LATENCY);
676             assertNotNull(String.format("No sync latency declared for ID %s", mIds[counter]),
677                     maxSyncLatencyValue);
678 
679             int maxSyncLatency = maxSyncLatencyValue;
680             final long MAX_LATENCY_BOUND = 4;
681             boolean haveFastSyncLatency =
682                 (maxSyncLatency <= MAX_LATENCY_BOUND) && (maxSyncLatency >= 0);
683 
684             if (haveBurstCapability) {
685                 assertTrue("Must have slow YUV size array when BURST_CAPTURE capability is defined!",
686                         slowYuvSizes != null);
687                 assertTrue(
688                         String.format("BURST-capable camera device %s does not have maximum YUV " +
689                                 "size that is at least max JPEG size",
690                                 mIds[counter]),
691                         haveMaxYuv);
692                 assertTrue(
693                         String.format("BURST-capable camera device %s max-resolution " +
694                                 "YUV frame rate is too slow" +
695                                 "(%d ns min frame duration reported, less than %d ns expected)",
696                                 mIds[counter], maxYuvRate, MIN_MAXSIZE_DURATION_BOUND_NS),
697                         haveMaxYuvRate);
698                 assertTrue(
699                         String.format("BURST-capable camera device %s >= 8MP YUV output " +
700                                 "frame rate is too slow" +
701                                 "(%d ns min frame duration reported, less than %d ns expected)",
702                                 mIds[counter], maxYuvRate, MIN_8MP_DURATION_BOUND_NS),
703                         haveFastYuvRate);
704                 assertTrue(
705                         String.format("BURST-capable camera device %s does not list an AE target " +
706                                 " FPS range with min FPS >= %f, for full-AUTO bursts",
707                                 mIds[counter], minYuvFps),
708                         haveFastAeTargetFps);
709                 assertTrue(
710                         String.format("BURST-capable camera device %s YUV sync latency is too long" +
711                                 "(%d frames reported, [0, %d] frames expected)",
712                                 mIds[counter], maxSyncLatency, MAX_LATENCY_BOUND),
713                         haveFastSyncLatency);
714                 assertTrue(
715                         String.format("BURST-capable camera device %s max YUV size %s should be" +
716                                 "close to active array size %s or cropped active array size %s",
717                                 mIds[counter], maxYuvSize.toString(), sensorSize.toString(),
718                                 croppedSensorSize.toString()),
719                         maxYuvMatchSensor);
720                 assertTrue(
721                         String.format("BURST-capable camera device %s does not support AE lock",
722                                 mIds[counter]),
723                         haveAeLock);
724                 assertTrue(
725                         String.format("BURST-capable camera device %s does not support AWB lock",
726                                 mIds[counter]),
727                         haveAwbLock);
728             } else {
729                 assertTrue("Must have null slow YUV size array when no BURST_CAPTURE capability!",
730                         slowYuvSizes == null);
731                 assertTrue(
732                         String.format("Camera device %s has all the requirements for BURST" +
733                                 " capability but does not report it!", mIds[counter]),
734                         !(haveMaxYuv && haveMaxYuvRate && haveFastYuvRate && haveFastAeTargetFps &&
735                                 haveFastSyncLatency && maxYuvMatchSensor &&
736                                 haveAeLock && haveAwbLock));
737             }
738 
739             counter++;
740         }
741     }
742 
743     /**
744      * Check reprocessing capabilities.
745      */
testReprocessingCharacteristics()746     public void testReprocessingCharacteristics() {
747         int counter = 0;
748 
749         for (CameraCharacteristics c : mCharacteristics) {
750             Log.i(TAG, "testReprocessingCharacteristics: Testing camera ID " + mIds[counter]);
751 
752             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
753             assertNotNull("android.request.availableCapabilities must never be null",
754                     capabilities);
755             boolean supportYUV = arrayContains(capabilities,
756                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
757             boolean supportOpaque = arrayContains(capabilities,
758                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
759             StreamConfigurationMap configs =
760                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
761             Integer maxNumInputStreams =
762                     c.get(CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS);
763             int[] availableEdgeModes = c.get(CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES);
764             int[] availableNoiseReductionModes = c.get(
765                     CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES);
766 
767             int[] inputFormats = configs.getInputFormats();
768 
769             boolean supportZslEdgeMode = false;
770             boolean supportZslNoiseReductionMode = false;
771             boolean supportHiQNoiseReductionMode = false;
772             boolean supportHiQEdgeMode = false;
773 
774             if (availableEdgeModes != null) {
775                 supportZslEdgeMode = Arrays.asList(CameraTestUtils.toObject(availableEdgeModes)).
776                         contains(CaptureRequest.EDGE_MODE_ZERO_SHUTTER_LAG);
777                 supportHiQEdgeMode = Arrays.asList(CameraTestUtils.toObject(availableEdgeModes)).
778                         contains(CaptureRequest.EDGE_MODE_HIGH_QUALITY);
779             }
780 
781             if (availableNoiseReductionModes != null) {
782                 supportZslNoiseReductionMode = Arrays.asList(
783                         CameraTestUtils.toObject(availableNoiseReductionModes)).contains(
784                         CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG);
785                 supportHiQNoiseReductionMode = Arrays.asList(
786                         CameraTestUtils.toObject(availableNoiseReductionModes)).contains(
787                         CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
788             }
789 
790             if (supportYUV || supportOpaque) {
791                 mCollector.expectTrue("Support reprocessing but max number of input stream is " +
792                         maxNumInputStreams, maxNumInputStreams != null && maxNumInputStreams > 0);
793                 mCollector.expectTrue("Support reprocessing but EDGE_MODE_ZERO_SHUTTER_LAG is " +
794                         "not supported", supportZslEdgeMode);
795                 mCollector.expectTrue("Support reprocessing but " +
796                         "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is not supported",
797                         supportZslNoiseReductionMode);
798 
799                 // For reprocessing, if we only require OFF and ZSL mode, it will be just like jpeg
800                 // encoding. We implicitly require FAST to make reprocessing meaningful, which means
801                 // that we also require HIGH_QUALITY.
802                 mCollector.expectTrue("Support reprocessing but EDGE_MODE_HIGH_QUALITY is " +
803                         "not supported", supportHiQEdgeMode);
804                 mCollector.expectTrue("Support reprocessing but " +
805                         "NOISE_REDUCTION_MODE_HIGH_QUALITY is not supported",
806                         supportHiQNoiseReductionMode);
807 
808                 // Verify mandatory input formats are supported
809                 mCollector.expectTrue("YUV_420_888 input must be supported for YUV reprocessing",
810                         !supportYUV || arrayContains(inputFormats, ImageFormat.YUV_420_888));
811                 mCollector.expectTrue("PRIVATE input must be supported for OPAQUE reprocessing",
812                         !supportOpaque || arrayContains(inputFormats, ImageFormat.PRIVATE));
813 
814                 // max capture stall must be reported if one of the reprocessing is supported.
815                 final int MAX_ALLOWED_STALL_FRAMES = 4;
816                 Integer maxCaptureStall = c.get(CameraCharacteristics.REPROCESS_MAX_CAPTURE_STALL);
817                 mCollector.expectTrue("max capture stall must be non-null and no larger than "
818                         + MAX_ALLOWED_STALL_FRAMES,
819                         maxCaptureStall != null && maxCaptureStall <= MAX_ALLOWED_STALL_FRAMES);
820 
821                 for (int input : inputFormats) {
822                     // Verify mandatory output formats are supported
823                     int[] outputFormats = configs.getValidOutputFormatsForInput(input);
824                     mCollector.expectTrue("YUV_420_888 output must be supported for reprocessing",
825                             arrayContains(outputFormats, ImageFormat.YUV_420_888));
826                     mCollector.expectTrue("JPEG output must be supported for reprocessing",
827                             arrayContains(outputFormats, ImageFormat.JPEG));
828 
829                     // Verify camera can output the reprocess input formats and sizes.
830                     Size[] inputSizes = configs.getInputSizes(input);
831                     Size[] outputSizes = configs.getOutputSizes(input);
832                     Size[] highResOutputSizes = configs.getHighResolutionOutputSizes(input);
833                     mCollector.expectTrue("no input size supported for format " + input,
834                             inputSizes.length > 0);
835                     mCollector.expectTrue("no output size supported for format " + input,
836                             outputSizes.length > 0);
837 
838                     for (Size inputSize : inputSizes) {
839                         mCollector.expectTrue("Camera must be able to output the supported " +
840                                 "reprocessing input size",
841                                 arrayContains(outputSizes, inputSize) ||
842                                 arrayContains(highResOutputSizes, inputSize));
843                     }
844                 }
845             } else {
846                 mCollector.expectTrue("Doesn't support reprocessing but report input format: " +
847                         Arrays.toString(inputFormats), inputFormats.length == 0);
848                 mCollector.expectTrue("Doesn't support reprocessing but max number of input " +
849                         "stream is " + maxNumInputStreams,
850                         maxNumInputStreams == null || maxNumInputStreams == 0);
851                 mCollector.expectTrue("Doesn't support reprocessing but " +
852                         "EDGE_MODE_ZERO_SHUTTER_LAG is supported", !supportZslEdgeMode);
853                 mCollector.expectTrue("Doesn't support reprocessing but " +
854                         "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is supported",
855                         !supportZslNoiseReductionMode);
856             }
857             counter++;
858         }
859     }
860 
861     /**
862      * Check depth output capability
863      */
testDepthOutputCharacteristics()864     public void testDepthOutputCharacteristics() {
865         int counter = 0;
866 
867         for (CameraCharacteristics c : mCharacteristics) {
868             Log.i(TAG, "testDepthOutputCharacteristics: Testing camera ID " + mIds[counter]);
869 
870             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
871             assertNotNull("android.request.availableCapabilities must never be null",
872                     capabilities);
873             boolean supportDepth = arrayContains(capabilities,
874                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT);
875             StreamConfigurationMap configs =
876                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
877 
878             int[] outputFormats = configs.getOutputFormats();
879             boolean hasDepth16 = arrayContains(outputFormats, ImageFormat.DEPTH16);
880 
881             Boolean depthIsExclusive = c.get(CameraCharacteristics.DEPTH_DEPTH_IS_EXCLUSIVE);
882 
883             float[] poseRotation = c.get(CameraCharacteristics.LENS_POSE_ROTATION);
884             float[] poseTranslation = c.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
885             Integer poseReference = c.get(CameraCharacteristics.LENS_POSE_REFERENCE);
886             float[] cameraIntrinsics = c.get(CameraCharacteristics.LENS_INTRINSIC_CALIBRATION);
887             float[] distortion = getLensDistortion(c);
888             Rect precorrectionArray = c.get(
889                 CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
890 
891             if (supportDepth) {
892                 mCollector.expectTrue("Supports DEPTH_OUTPUT but does not support DEPTH16",
893                         hasDepth16);
894                 if (hasDepth16) {
895                     Size[] depthSizes = configs.getOutputSizes(ImageFormat.DEPTH16);
896                     mCollector.expectTrue("Supports DEPTH_OUTPUT but no sizes for DEPTH16 supported!",
897                             depthSizes != null && depthSizes.length > 0);
898                     if (depthSizes != null) {
899                         for (Size depthSize : depthSizes) {
900                             mCollector.expectTrue("All depth16 sizes must be positive",
901                                     depthSize.getWidth() > 0 && depthSize.getHeight() > 0);
902                             long minFrameDuration = configs.getOutputMinFrameDuration(
903                                     ImageFormat.DEPTH16, depthSize);
904                             mCollector.expectTrue("Non-negative min frame duration for depth size "
905                                     + depthSize + " expected, got " + minFrameDuration,
906                                     minFrameDuration >= 0);
907                             long stallDuration = configs.getOutputStallDuration(
908                                     ImageFormat.DEPTH16, depthSize);
909                             mCollector.expectTrue("Non-negative stall duration for depth size "
910                                     + depthSize + " expected, got " + stallDuration,
911                                     stallDuration >= 0);
912                         }
913                     }
914                 }
915                 if (arrayContains(outputFormats, ImageFormat.DEPTH_POINT_CLOUD)) {
916                     Size[] depthCloudSizes = configs.getOutputSizes(ImageFormat.DEPTH_POINT_CLOUD);
917                     mCollector.expectTrue("Supports DEPTH_POINT_CLOUD " +
918                             "but no sizes for DEPTH_POINT_CLOUD supported!",
919                             depthCloudSizes != null && depthCloudSizes.length > 0);
920                     if (depthCloudSizes != null) {
921                         for (Size depthCloudSize : depthCloudSizes) {
922                             mCollector.expectTrue("All depth point cloud sizes must be nonzero",
923                                     depthCloudSize.getWidth() > 0);
924                             mCollector.expectTrue("All depth point cloud sizes must be N x 1",
925                                     depthCloudSize.getHeight() == 1);
926                             long minFrameDuration = configs.getOutputMinFrameDuration(
927                                     ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
928                             mCollector.expectTrue("Non-negative min frame duration for depth size "
929                                     + depthCloudSize + " expected, got " + minFrameDuration,
930                                     minFrameDuration >= 0);
931                             long stallDuration = configs.getOutputStallDuration(
932                                     ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
933                             mCollector.expectTrue("Non-negative stall duration for depth size "
934                                     + depthCloudSize + " expected, got " + stallDuration,
935                                     stallDuration >= 0);
936                         }
937                     }
938                 }
939 
940                 mCollector.expectTrue("Supports DEPTH_OUTPUT but DEPTH_IS_EXCLUSIVE is not defined",
941                         depthIsExclusive != null);
942 
943                 verifyLensCalibration(poseRotation, poseTranslation, poseReference,
944                         cameraIntrinsics, distortion, precorrectionArray);
945 
946             } else {
947                 boolean hasFields =
948                     hasDepth16 && (poseTranslation != null) &&
949                     (poseRotation != null) && (cameraIntrinsics != null) &&
950                     (distortion != null) && (depthIsExclusive != null);
951 
952                 mCollector.expectTrue(
953                         "All necessary depth fields defined, but DEPTH_OUTPUT capability is not listed",
954                         !hasFields);
955             }
956             counter++;
957         }
958     }
959 
verifyLensCalibration(float[] poseRotation, float[] poseTranslation, Integer poseReference, float[] cameraIntrinsics, float[] distortion, Rect precorrectionArray)960     private void verifyLensCalibration(float[] poseRotation, float[] poseTranslation,
961             Integer poseReference, float[] cameraIntrinsics, float[] distortion,
962             Rect precorrectionArray) {
963 
964         mCollector.expectTrue(
965             "LENS_POSE_ROTATION not right size",
966             poseRotation != null && poseRotation.length == 4);
967         mCollector.expectTrue(
968             "LENS_POSE_TRANSLATION not right size",
969             poseTranslation != null && poseTranslation.length == 3);
970         mCollector.expectTrue(
971             "LENS_POSE_REFERENCE is not defined",
972             poseReference != null);
973         mCollector.expectTrue(
974             "LENS_INTRINSIC_CALIBRATION not right size",
975             cameraIntrinsics != null && cameraIntrinsics.length == 5);
976         mCollector.expectTrue(
977             "LENS_DISTORTION not right size",
978             distortion != null && distortion.length == 6);
979 
980         if (poseRotation != null && poseRotation.length == 4) {
981             float normSq =
982                     poseRotation[0] * poseRotation[0] +
983                     poseRotation[1] * poseRotation[1] +
984                     poseRotation[2] * poseRotation[2] +
985                     poseRotation[3] * poseRotation[3];
986             mCollector.expectTrue(
987                 "LENS_POSE_ROTATION quarternion must be unit-length",
988                 0.9999f < normSq && normSq < 1.0001f);
989 
990             // TODO: Cross-validate orientation/facing and poseRotation
991         }
992 
993         if (poseTranslation != null && poseTranslation.length == 3) {
994             float normSq =
995                     poseTranslation[0] * poseTranslation[0] +
996                     poseTranslation[1] * poseTranslation[1] +
997                     poseTranslation[2] * poseTranslation[2];
998             mCollector.expectTrue("Pose translation is larger than 1 m",
999                     normSq < 1.f);
1000         }
1001 
1002         if (poseReference != null) {
1003             int ref = poseReference;
1004             boolean validReference = false;
1005             switch (ref) {
1006                 case CameraCharacteristics.LENS_POSE_REFERENCE_PRIMARY_CAMERA:
1007                 case CameraCharacteristics.LENS_POSE_REFERENCE_GYROSCOPE:
1008                     // Allowed values
1009                     validReference = true;
1010                     break;
1011                 default:
1012             }
1013             mCollector.expectTrue("POSE_REFERENCE has unknown value", validReference);
1014         }
1015 
1016         mCollector.expectTrue("Does not have precorrection active array defined",
1017                 precorrectionArray != null);
1018 
1019         if (cameraIntrinsics != null && precorrectionArray != null) {
1020             float fx = cameraIntrinsics[0];
1021             float fy = cameraIntrinsics[1];
1022             float cx = cameraIntrinsics[2];
1023             float cy = cameraIntrinsics[3];
1024             float s = cameraIntrinsics[4];
1025             mCollector.expectTrue("Optical center expected to be within precorrection array",
1026                     0 <= cx && cx < precorrectionArray.width() &&
1027                     0 <= cy && cy < precorrectionArray.height());
1028 
1029             // TODO: Verify focal lengths and skew are reasonable
1030         }
1031 
1032         if (distortion != null) {
1033             // TODO: Verify radial distortion
1034         }
1035 
1036     }
1037 
1038     /**
1039      * Cross-check StreamConfigurationMap output
1040      */
testStreamConfigurationMap()1041     public void testStreamConfigurationMap() throws Exception {
1042         int counter = 0;
1043         for (CameraCharacteristics c : mCharacteristics) {
1044             Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + mIds[counter]);
1045             StreamConfigurationMap config =
1046                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1047             assertNotNull(String.format("No stream configuration map found for: ID %s",
1048                             mIds[counter]), config);
1049 
1050             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1051             assertNotNull("android.request.availableCapabilities must never be null",
1052                     actualCapabilities);
1053 
1054             if (arrayContains(actualCapabilities, BC)) {
1055                 assertTrue("ImageReader must be supported",
1056                     config.isOutputSupportedFor(android.media.ImageReader.class));
1057                 assertTrue("MediaRecorder must be supported",
1058                     config.isOutputSupportedFor(android.media.MediaRecorder.class));
1059                 assertTrue("MediaCodec must be supported",
1060                     config.isOutputSupportedFor(android.media.MediaCodec.class));
1061                 assertTrue("Allocation must be supported",
1062                     config.isOutputSupportedFor(android.renderscript.Allocation.class));
1063                 assertTrue("SurfaceHolder must be supported",
1064                     config.isOutputSupportedFor(android.view.SurfaceHolder.class));
1065                 assertTrue("SurfaceTexture must be supported",
1066                     config.isOutputSupportedFor(android.graphics.SurfaceTexture.class));
1067 
1068                 assertTrue("YUV_420_888 must be supported",
1069                     config.isOutputSupportedFor(ImageFormat.YUV_420_888));
1070                 assertTrue("JPEG must be supported",
1071                     config.isOutputSupportedFor(ImageFormat.JPEG));
1072             } else {
1073                 assertTrue("YUV_420_88 may not be supported if BACKWARD_COMPATIBLE capability is not listed",
1074                     !config.isOutputSupportedFor(ImageFormat.YUV_420_888));
1075                 assertTrue("JPEG may not be supported if BACKWARD_COMPATIBLE capability is not listed",
1076                     !config.isOutputSupportedFor(ImageFormat.JPEG));
1077             }
1078 
1079             // Check RAW
1080 
1081             if (arrayContains(actualCapabilities,
1082                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
1083                 assertTrue("RAW_SENSOR must be supported if RAW capability is advertised",
1084                     config.isOutputSupportedFor(ImageFormat.RAW_SENSOR));
1085             }
1086 
1087             // Cross check public formats and sizes
1088 
1089             int[] supportedFormats = config.getOutputFormats();
1090             for (int format : supportedFormats) {
1091                 assertTrue("Format " + format + " fails cross check",
1092                         config.isOutputSupportedFor(format));
1093                 List<Size> supportedSizes = CameraTestUtils.getAscendingOrderSizes(
1094                         Arrays.asList(config.getOutputSizes(format)), /*ascending*/true);
1095                 if (arrayContains(actualCapabilities,
1096                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
1097                     supportedSizes.addAll(
1098                         Arrays.asList(config.getHighResolutionOutputSizes(format)));
1099                     supportedSizes = CameraTestUtils.getAscendingOrderSizes(
1100                         supportedSizes, /*ascending*/true);
1101                 }
1102                 assertTrue("Supported format " + format + " has no sizes listed",
1103                         supportedSizes.size() > 0);
1104                 for (int i = 0; i < supportedSizes.size(); i++) {
1105                     Size size = supportedSizes.get(i);
1106                     if (VERBOSE) {
1107                         Log.v(TAG,
1108                                 String.format("Testing camera %s, format %d, size %s",
1109                                         mIds[counter], format, size.toString()));
1110                     }
1111 
1112                     long stallDuration = config.getOutputStallDuration(format, size);
1113                     switch(format) {
1114                         case ImageFormat.YUV_420_888:
1115                             assertTrue("YUV_420_888 may not have a non-zero stall duration",
1116                                     stallDuration == 0);
1117                             break;
1118                         case ImageFormat.JPEG:
1119                         case ImageFormat.RAW_SENSOR:
1120                             final float TOLERANCE_FACTOR = 2.0f;
1121                             long prevDuration = 0;
1122                             if (i > 0) {
1123                                 prevDuration = config.getOutputStallDuration(
1124                                         format, supportedSizes.get(i - 1));
1125                             }
1126                             long nextDuration = Long.MAX_VALUE;
1127                             if (i < (supportedSizes.size() - 1)) {
1128                                 nextDuration = config.getOutputStallDuration(
1129                                         format, supportedSizes.get(i + 1));
1130                             }
1131                             long curStallDuration = config.getOutputStallDuration(format, size);
1132                             // Stall duration should be in a reasonable range: larger size should
1133                             // normally have larger stall duration.
1134                             mCollector.expectInRange("Stall duration (format " + format +
1135                                     " and size " + size + ") is not in the right range",
1136                                     curStallDuration,
1137                                     (long) (prevDuration / TOLERANCE_FACTOR),
1138                                     (long) (nextDuration * TOLERANCE_FACTOR));
1139                             break;
1140                         default:
1141                             assertTrue("Negative stall duration for format " + format,
1142                                     stallDuration >= 0);
1143                             break;
1144                     }
1145                     long minDuration = config.getOutputMinFrameDuration(format, size);
1146                     if (arrayContains(actualCapabilities,
1147                             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
1148                         assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
1149                                 + "format " + format + " for size " + size + " minDuration " +
1150                                 minDuration,
1151                                 minDuration > 0);
1152                     } else {
1153                         assertTrue("Need non-negative min frame duration for format " + format,
1154                                 minDuration >= 0);
1155                     }
1156 
1157                     // todo: test opaque image reader when it's supported.
1158                     if (format != ImageFormat.PRIVATE) {
1159                         ImageReader testReader = ImageReader.newInstance(
1160                             size.getWidth(),
1161                             size.getHeight(),
1162                             format,
1163                             1);
1164                         Surface testSurface = testReader.getSurface();
1165 
1166                         assertTrue(
1167                             String.format("isOutputSupportedFor fails for config %s, format %d",
1168                                     size.toString(), format),
1169                             config.isOutputSupportedFor(testSurface));
1170 
1171                         testReader.close();
1172                     }
1173                 } // sizes
1174 
1175                 // Try an invalid size in this format, should round
1176                 Size invalidSize = findInvalidSize(supportedSizes);
1177                 int MAX_ROUNDING_WIDTH = 1920;
1178                 // todo: test opaque image reader when it's supported.
1179                 if (format != ImageFormat.PRIVATE &&
1180                         invalidSize.getWidth() <= MAX_ROUNDING_WIDTH) {
1181                     ImageReader testReader = ImageReader.newInstance(
1182                                                                      invalidSize.getWidth(),
1183                                                                      invalidSize.getHeight(),
1184                                                                      format,
1185                                                                      1);
1186                     Surface testSurface = testReader.getSurface();
1187 
1188                     assertTrue(
1189                                String.format("isOutputSupportedFor fails for config %s, %d",
1190                                        invalidSize.toString(), format),
1191                                config.isOutputSupportedFor(testSurface));
1192 
1193                     testReader.close();
1194                 }
1195             } // formats
1196 
1197             // Cross-check opaque format and sizes
1198             if (arrayContains(actualCapabilities, BC)) {
1199                 SurfaceTexture st = new SurfaceTexture(1);
1200                 Surface surf = new Surface(st);
1201 
1202                 Size[] opaqueSizes = CameraTestUtils.getSupportedSizeForClass(SurfaceTexture.class,
1203                         mIds[counter], mCameraManager);
1204                 assertTrue("Opaque format has no sizes listed",
1205                         opaqueSizes.length > 0);
1206                 for (Size size : opaqueSizes) {
1207                     long stallDuration = config.getOutputStallDuration(SurfaceTexture.class, size);
1208                     assertTrue("Opaque output may not have a non-zero stall duration",
1209                             stallDuration == 0);
1210 
1211                     long minDuration = config.getOutputMinFrameDuration(SurfaceTexture.class, size);
1212                     if (arrayContains(actualCapabilities,
1213                                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
1214                         assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
1215                                 + "opaque format",
1216                                 minDuration > 0);
1217                     } else {
1218                         assertTrue("Need non-negative min frame duration for opaque format ",
1219                                 minDuration >= 0);
1220                     }
1221                     st.setDefaultBufferSize(size.getWidth(), size.getHeight());
1222 
1223                     assertTrue(
1224                             String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
1225                                     size.toString()),
1226                             config.isOutputSupportedFor(surf));
1227 
1228                 } // opaque sizes
1229 
1230                 // Try invalid opaque size, should get rounded
1231                 Size invalidSize = findInvalidSize(opaqueSizes);
1232                 st.setDefaultBufferSize(invalidSize.getWidth(), invalidSize.getHeight());
1233                 assertTrue(
1234                         String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
1235                                 invalidSize.toString()),
1236                         config.isOutputSupportedFor(surf));
1237 
1238             }
1239             counter++;
1240         } // mCharacteristics
1241     }
1242 
1243     /**
1244      * Test high speed capability and cross-check the high speed sizes and fps ranges from
1245      * the StreamConfigurationMap.
1246      */
testConstrainedHighSpeedCapability()1247     public void testConstrainedHighSpeedCapability() throws Exception {
1248         int counter = 0;
1249         for (CameraCharacteristics c : mCharacteristics) {
1250             int[] capabilities = CameraTestUtils.getValueNotNull(
1251                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1252             boolean supportHighSpeed = arrayContains(capabilities, CONSTRAINED_HIGH_SPEED);
1253             if (supportHighSpeed) {
1254                 StreamConfigurationMap config =
1255                         CameraTestUtils.getValueNotNull(
1256                                 c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1257                 List<Size> highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes());
1258                 assertTrue("High speed sizes shouldn't be empty", highSpeedSizes.size() > 0);
1259                 Size[] allSizes = CameraTestUtils.getSupportedSizeForFormat(ImageFormat.PRIVATE,
1260                         mIds[counter], mCameraManager);
1261                 assertTrue("Normal size for PRIVATE format shouldn't be null or empty",
1262                         allSizes != null && allSizes.length > 0);
1263                 for (Size size: highSpeedSizes) {
1264                     // The sizes must be a subset of the normal sizes
1265                     assertTrue("High speed size " + size +
1266                             " must be part of normal sizes " + Arrays.toString(allSizes),
1267                             Arrays.asList(allSizes).contains(size));
1268 
1269                     // Sanitize the high speed FPS ranges for each size
1270                     List<Range<Integer>> ranges =
1271                             Arrays.asList(config.getHighSpeedVideoFpsRangesFor(size));
1272                     for (Range<Integer> range : ranges) {
1273                         assertTrue("The range " + range + " doesn't satisfy the"
1274                                 + " min/max boundary requirements.",
1275                                 range.getLower() >= HIGH_SPEED_FPS_LOWER_MIN &&
1276                                 range.getUpper() >= HIGH_SPEED_FPS_UPPER_MIN);
1277                         assertTrue("The range " + range + " should be multiple of 30fps",
1278                                 range.getLower() % 30 == 0 && range.getUpper() % 30 == 0);
1279                         // If the range is fixed high speed range, it should contain the
1280                         // [30, fps_max] in the high speed range list; if it's variable FPS range,
1281                         // the corresponding fixed FPS Range must be included in the range list.
1282                         if (range.getLower() == range.getUpper()) {
1283                             Range<Integer> variableRange = new Range<Integer>(30, range.getUpper());
1284                             assertTrue("The variable FPS range " + variableRange +
1285                                     " shoould be included in the high speed ranges for size " +
1286                                     size, ranges.contains(variableRange));
1287                         } else {
1288                             Range<Integer> fixedRange =
1289                                     new Range<Integer>(range.getUpper(), range.getUpper());
1290                             assertTrue("The fixed FPS range " + fixedRange +
1291                                     " shoould be included in the high speed ranges for size " +
1292                                     size, ranges.contains(fixedRange));
1293                         }
1294                     }
1295                 }
1296                 // If the device advertise some high speed profiles, the sizes and FPS ranges
1297                 // should be advertise by the camera.
1298                 for (int quality = CamcorderProfile.QUALITY_HIGH_SPEED_480P;
1299                         quality <= CamcorderProfile.QUALITY_HIGH_SPEED_2160P; quality++) {
1300                     int cameraId = Integer.valueOf(mIds[counter]);
1301                     if (CamcorderProfile.hasProfile(cameraId, quality)) {
1302                         CamcorderProfile profile = CamcorderProfile.get(cameraId, quality);
1303                         Size camcorderProfileSize =
1304                                 new Size(profile.videoFrameWidth, profile.videoFrameHeight);
1305                         assertTrue("CamcorderPrfile size " + camcorderProfileSize +
1306                                 " must be included in the high speed sizes " +
1307                                 Arrays.toString(highSpeedSizes.toArray()),
1308                                 highSpeedSizes.contains(camcorderProfileSize));
1309                         Range<Integer> camcorderFpsRange =
1310                                 new Range<Integer>(profile.videoFrameRate, profile.videoFrameRate);
1311                         List<Range<Integer>> allRanges =
1312                                 Arrays.asList(config.getHighSpeedVideoFpsRangesFor(
1313                                         camcorderProfileSize));
1314                         assertTrue("Camcorder fps range " + camcorderFpsRange +
1315                                 " should be included by high speed fps ranges " +
1316                                 Arrays.toString(allRanges.toArray()),
1317                                 allRanges.contains(camcorderFpsRange));
1318                     }
1319                 }
1320             }
1321             counter++;
1322         }
1323     }
1324 
1325     /**
1326      * Sanity check of optical black regions.
1327      */
testOpticalBlackRegions()1328     public void testOpticalBlackRegions() {
1329         int counter = 0;
1330         for (CameraCharacteristics c : mCharacteristics) {
1331             List<CaptureResult.Key<?>> resultKeys = c.getAvailableCaptureResultKeys();
1332             boolean hasDynamicBlackLevel =
1333                     resultKeys.contains(CaptureResult.SENSOR_DYNAMIC_BLACK_LEVEL);
1334             boolean hasDynamicWhiteLevel =
1335                     resultKeys.contains(CaptureResult.SENSOR_DYNAMIC_WHITE_LEVEL);
1336             boolean hasFixedBlackLevel =
1337                     c.getKeys().contains(CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN);
1338             boolean hasFixedWhiteLevel =
1339                     c.getKeys().contains(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
1340             // The black and white levels should be either all supported or none of them is
1341             // supported.
1342             mCollector.expectTrue("Dynamic black and white level should be all or none of them"
1343                     + " be supported", hasDynamicWhiteLevel == hasDynamicBlackLevel);
1344             mCollector.expectTrue("Fixed black and white level should be all or none of them"
1345                     + " be supported", hasFixedBlackLevel == hasFixedWhiteLevel);
1346             mCollector.expectTrue("Fixed black level should be supported if dynamic black"
1347                     + " level is supported", !hasDynamicBlackLevel || hasFixedBlackLevel);
1348 
1349             if (c.getKeys().contains(CameraCharacteristics.SENSOR_OPTICAL_BLACK_REGIONS)) {
1350                 // Regions shouldn't be null or empty.
1351                 Rect[] regions = CameraTestUtils.getValueNotNull(c,
1352                         CameraCharacteristics.SENSOR_OPTICAL_BLACK_REGIONS);
1353                 CameraTestUtils.assertArrayNotEmpty(regions, "Optical back region arrays must not"
1354                         + " be empty");
1355 
1356                 // Dynamic black level should be supported if the optical black region is
1357                 // advertised.
1358                 mCollector.expectTrue("Dynamic black and white level keys should be advertised in "
1359                         + "available capture result key list", hasDynamicWhiteLevel);
1360 
1361                 // Range check.
1362                 for (Rect region : regions) {
1363                     mCollector.expectTrue("Camera " + mIds[counter] + ": optical black region" +
1364                             " shouldn't be empty!", !region.isEmpty());
1365                     mCollector.expectGreaterOrEqual("Optical black region left", 0/*expected*/,
1366                             region.left/*actual*/);
1367                     mCollector.expectGreaterOrEqual("Optical black region top", 0/*expected*/,
1368                             region.top/*actual*/);
1369                     mCollector.expectTrue("Optical black region left/right/width/height must be"
1370                             + " even number, otherwise, the bayer CFA pattern in this region will"
1371                             + " be messed up",
1372                             region.left % 2 == 0 && region.top % 2 == 0 &&
1373                             region.width() % 2 == 0 && region.height() % 2 == 0);
1374                     mCollector.expectGreaterOrEqual("Optical black region top", 0/*expected*/,
1375                             region.top/*actual*/);
1376                     Size size = CameraTestUtils.getValueNotNull(c,
1377                             CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
1378                     mCollector.expectLessOrEqual("Optical black region width",
1379                             size.getWidth()/*expected*/, region.width());
1380                     mCollector.expectLessOrEqual("Optical black region height",
1381                             size.getHeight()/*expected*/, region.height());
1382                     Rect activeArray = CameraTestUtils.getValueNotNull(c,
1383                             CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1384                     mCollector.expectTrue("Optical black region" + region + " should be outside of"
1385                             + " active array " + activeArray,
1386                             !region.intersect(activeArray));
1387                     // Region need to be disjoint:
1388                     for (Rect region2 : regions) {
1389                         mCollector.expectTrue("Optical black region" + region + " should have no "
1390                                 + "overlap with " + region2,
1391                                 region == region2 || !region.intersect(region2));
1392                     }
1393                 }
1394             } else {
1395                 Log.i(TAG, "Camera " + mIds[counter] + " doesn't support optical black regions,"
1396                         + " skip the region test");
1397             }
1398             counter++;
1399         }
1400     }
1401 
1402     /**
1403      * Check Logical camera capability
1404      */
testLogicalCameraCharacteristics()1405     public void testLogicalCameraCharacteristics() throws Exception {
1406         int counter = 0;
1407         List<String> cameraIdList = Arrays.asList(mIds);
1408 
1409         for (CameraCharacteristics c : mCharacteristics) {
1410             int[] capabilities = CameraTestUtils.getValueNotNull(
1411                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1412             boolean supportLogicalCamera = arrayContains(capabilities,
1413                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA);
1414             if (supportLogicalCamera) {
1415                 Set<String> physicalCameraIds = c.getPhysicalCameraIds();
1416                 assertNotNull("android.logicalCam.physicalCameraIds shouldn't be null",
1417                     physicalCameraIds);
1418                 assertTrue("Logical camera must contain at least 2 physical camera ids",
1419                     physicalCameraIds.size() >= 2);
1420 
1421                 mCollector.expectKeyValueInRange(c,
1422                         CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE,
1423                         CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE,
1424                         CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED);
1425 
1426                 Integer timestampSource = c.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
1427                 for (String physicalCameraId : physicalCameraIds) {
1428                     assertNotNull("Physical camera id shouldn't be null", physicalCameraId);
1429                     assertTrue(
1430                             String.format("Physical camera id %s shouldn't be the same as logical"
1431                                     + " camera id %s", physicalCameraId, mIds[counter]),
1432                             physicalCameraId != mIds[counter]);
1433                     assertTrue(
1434                             String.format("Physical camera id %s should be in available camera ids",
1435                                     physicalCameraId),
1436                             cameraIdList.contains(physicalCameraId));
1437 
1438                     //validation for depth static metadata of physical cameras
1439                     CameraCharacteristics pc =
1440                             mCameraManager.getCameraCharacteristics(physicalCameraId);
1441 
1442                     float[] poseRotation = pc.get(CameraCharacteristics.LENS_POSE_ROTATION);
1443                     float[] poseTranslation = pc.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
1444                     Integer poseReference = pc.get(CameraCharacteristics.LENS_POSE_REFERENCE);
1445                     float[] cameraIntrinsics = pc.get(
1446                             CameraCharacteristics.LENS_INTRINSIC_CALIBRATION);
1447                     float[] distortion = getLensDistortion(pc);
1448                     Rect precorrectionArray = pc.get(
1449                             CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1450 
1451                     verifyLensCalibration(poseRotation, poseTranslation, poseReference,
1452                             cameraIntrinsics, distortion, precorrectionArray);
1453 
1454                     Integer timestampSourcePhysical =
1455                             pc.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
1456                     mCollector.expectEquals("Logical camera and physical cameras must have same " +
1457                             "timestamp source", timestampSource, timestampSourcePhysical);
1458                 }
1459             }
1460             counter++;
1461         }
1462     }
1463 
1464     /**
1465      * Check monochrome camera capability
1466      */
testMonochromeCharacteristics()1467     public void testMonochromeCharacteristics() {
1468         int counter = 0;
1469 
1470         for (CameraCharacteristics c : mCharacteristics) {
1471             Log.i(TAG, "testMonochromeCharacteristics: Testing camera ID " + mIds[counter]);
1472 
1473             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1474             assertNotNull("android.request.availableCapabilities must never be null",
1475                     capabilities);
1476             boolean supportMonochrome = arrayContains(capabilities, MONOCHROME);
1477 
1478             if (!supportMonochrome) {
1479                 continue;
1480             }
1481 
1482             assertTrue("Monochrome camera must have BACKWARD_COMPATIBLE capability",
1483                     arrayContains(capabilities, BC));
1484             assertTrue("Monochrome camera must not have RAW capability",
1485                     !arrayContains(capabilities, RAW));
1486             assertTrue("Monochrome camera must not have MANUAL_POST_PROCESSING capability",
1487                     !arrayContains(capabilities, MANUAL_POSTPROC));
1488 
1489             // Check that awbSupportedModes only contains AUTO
1490             int[] awbAvailableModes = c.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES);
1491             assertTrue("availableAwbModes must not be null", awbAvailableModes != null);
1492             assertTrue("availableAwbModes must contain only AUTO", awbAvailableModes.length == 1 &&
1493                     awbAvailableModes[0] == CaptureRequest.CONTROL_AWB_MODE_AUTO);
1494         }
1495     }
1496 
1497     /**
1498      * Get lens distortion coefficients, as a list of 6 floats; returns null if no valid
1499      * distortion field is available
1500      */
getLensDistortion(CameraCharacteristics c)1501     private float[] getLensDistortion(CameraCharacteristics c) {
1502         float[] distortion = null;
1503         float[] newDistortion = c.get(CameraCharacteristics.LENS_DISTORTION);
1504         if (Build.VERSION.FIRST_SDK_INT > Build.VERSION_CODES.O_MR1 || newDistortion != null) {
1505             // New devices need to use fixed radial distortion definition; old devices can
1506             // opt-in to it
1507             if (newDistortion != null && newDistortion.length == 5) {
1508                 distortion = new float[6];
1509                 distortion[0] = 1.0f;
1510                 for (int i = 1; i < 6; i++) {
1511                     distortion[i] = newDistortion[i-1];
1512                 }
1513             }
1514         } else {
1515             // Select old field only if on older first SDK and new definition not available
1516             distortion = c.get(CameraCharacteristics.LENS_RADIAL_DISTORTION);
1517         }
1518         return distortion;
1519     }
1520 
1521     /**
1522      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
1523      */
findInvalidSize(Size[] goodSizes)1524     private Size findInvalidSize(Size[] goodSizes) {
1525         return findInvalidSize(Arrays.asList(goodSizes));
1526     }
1527 
1528     /**
1529      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
1530      */
findInvalidSize(List<Size> goodSizes)1531     private Size findInvalidSize(List<Size> goodSizes) {
1532         Size invalidSize = new Size(goodSizes.get(0).getWidth() + 1, goodSizes.get(0).getHeight());
1533         while(goodSizes.contains(invalidSize)) {
1534             invalidSize = new Size(invalidSize.getWidth() + 1, invalidSize.getHeight());
1535         }
1536         return invalidSize;
1537     }
1538 
1539     /**
1540      * Check key is present in characteristics if the hardware level is at least {@code hwLevel};
1541      * check that the key is present if the actual capabilities are one of {@code capabilities}.
1542      *
1543      * @return value of the {@code key} from {@code c}
1544      */
expectKeyAvailable(CameraCharacteristics c, CameraCharacteristics.Key<T> key, int hwLevel, int... capabilities)1545     private <T> T expectKeyAvailable(CameraCharacteristics c, CameraCharacteristics.Key<T> key,
1546             int hwLevel, int... capabilities) {
1547 
1548         Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
1549         assertNotNull("android.info.supportedHardwareLevel must never be null", actualHwLevel);
1550 
1551         int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1552         assertNotNull("android.request.availableCapabilities must never be null",
1553                 actualCapabilities);
1554 
1555         List<Key<?>> allKeys = c.getKeys();
1556 
1557         T value = c.get(key);
1558 
1559         // For LIMITED-level targeted keys, rely on capability check, not level
1560         if ((compareHardwareLevel(actualHwLevel, hwLevel) >= 0) && (hwLevel != LIMITED)) {
1561             mCollector.expectTrue(
1562                     String.format("Key (%s) must be in characteristics for this hardware level " +
1563                             "(required minimal HW level %s, actual HW level %s)",
1564                             key.getName(), toStringHardwareLevel(hwLevel),
1565                             toStringHardwareLevel(actualHwLevel)),
1566                     value != null);
1567             mCollector.expectTrue(
1568                     String.format("Key (%s) must be in characteristics list of keys for this " +
1569                             "hardware level (required minimal HW level %s, actual HW level %s)",
1570                             key.getName(), toStringHardwareLevel(hwLevel),
1571                             toStringHardwareLevel(actualHwLevel)),
1572                     allKeys.contains(key));
1573         } else if (arrayContainsAnyOf(actualCapabilities, capabilities)) {
1574             if (!(hwLevel == LIMITED && compareHardwareLevel(actualHwLevel, hwLevel) < 0)) {
1575                 // Don't enforce LIMITED-starting keys on LEGACY level, even if cap is defined
1576                 mCollector.expectTrue(
1577                     String.format("Key (%s) must be in characteristics for these capabilities " +
1578                             "(required capabilities %s, actual capabilities %s)",
1579                             key.getName(), Arrays.toString(capabilities),
1580                             Arrays.toString(actualCapabilities)),
1581                     value != null);
1582                 mCollector.expectTrue(
1583                     String.format("Key (%s) must be in characteristics list of keys for " +
1584                             "these capabilities (required capabilities %s, actual capabilities %s)",
1585                             key.getName(), Arrays.toString(capabilities),
1586                             Arrays.toString(actualCapabilities)),
1587                     allKeys.contains(key));
1588             }
1589         } else {
1590             if (actualHwLevel == LEGACY && hwLevel != OPT) {
1591                 if (value != null || allKeys.contains(key)) {
1592                     Log.w(TAG, String.format(
1593                             "Key (%s) is not required for LEGACY devices but still appears",
1594                             key.getName()));
1595                 }
1596             }
1597             // OK: Key may or may not be present.
1598         }
1599         return value;
1600     }
1601 
arrayContains(int[] arr, int needle)1602     private static boolean arrayContains(int[] arr, int needle) {
1603         if (arr == null) {
1604             return false;
1605         }
1606 
1607         for (int elem : arr) {
1608             if (elem == needle) {
1609                 return true;
1610             }
1611         }
1612 
1613         return false;
1614     }
1615 
arrayContains(T[] arr, T needle)1616     private static <T> boolean arrayContains(T[] arr, T needle) {
1617         if (arr == null) {
1618             return false;
1619         }
1620 
1621         for (T elem : arr) {
1622             if (elem.equals(needle)) {
1623                 return true;
1624             }
1625         }
1626 
1627         return false;
1628     }
1629 
arrayContainsAnyOf(int[] arr, int[] needles)1630     private static boolean arrayContainsAnyOf(int[] arr, int[] needles) {
1631         for (int needle : needles) {
1632             if (arrayContains(arr, needle)) {
1633                 return true;
1634             }
1635         }
1636         return false;
1637     }
1638 
1639     /**
1640      * The key name has a prefix of either "android." or a valid TLD; other prefixes are not valid.
1641      */
assertKeyPrefixValid(String keyName)1642     private static void assertKeyPrefixValid(String keyName) {
1643         assertStartsWithAndroidOrTLD(
1644                 "All metadata keys must start with 'android.' (built-in keys) " +
1645                 "or valid TLD (vendor-extended keys)", keyName);
1646     }
1647 
assertTrueForKey(String msg, CameraCharacteristics.Key<?> key, boolean actual)1648     private static void assertTrueForKey(String msg, CameraCharacteristics.Key<?> key,
1649             boolean actual) {
1650         assertTrue(msg + " (key = '" + key.getName() + "')", actual);
1651     }
1652 
assertOneOf(String msg, T[] expected, T actual)1653     private static <T> void assertOneOf(String msg, T[] expected, T actual) {
1654         for (int i = 0; i < expected.length; ++i) {
1655             if (Objects.equals(expected[i], actual)) {
1656                 return;
1657             }
1658         }
1659 
1660         fail(String.format("%s: (expected one of %s, actual %s)",
1661                 msg, Arrays.toString(expected), actual));
1662     }
1663 
assertStartsWithAndroidOrTLD(String msg, String keyName)1664     private static <T> void assertStartsWithAndroidOrTLD(String msg, String keyName) {
1665         String delimiter = ".";
1666         if (keyName.startsWith(PREFIX_ANDROID + delimiter)) {
1667             return;
1668         }
1669         Pattern tldPattern = Pattern.compile(Patterns.TOP_LEVEL_DOMAIN_STR);
1670         Matcher match = tldPattern.matcher(keyName);
1671         if (match.find(0) && (0 == match.start()) && (!match.hitEnd())) {
1672             if (keyName.regionMatches(match.end(), delimiter, 0, delimiter.length())) {
1673                 return;
1674             }
1675         }
1676 
1677         fail(String.format("%s: (expected to start with %s or valid TLD, but value was %s)",
1678                 msg, PREFIX_ANDROID + delimiter, keyName));
1679     }
1680 
1681     /** Return a positive int if left > right, 0 if left==right, negative int if left < right */
compareHardwareLevel(int left, int right)1682     private static int compareHardwareLevel(int left, int right) {
1683         return remapHardwareLevel(left) - remapHardwareLevel(right);
1684     }
1685 
1686     /** Remap HW levels worst<->best, 0 = LEGACY, 1 = LIMITED, 2 = FULL, ..., N = LEVEL_N */
remapHardwareLevel(int level)1687     private static int remapHardwareLevel(int level) {
1688         switch (level) {
1689             case OPT:
1690                 return Integer.MAX_VALUE;
1691             case LEGACY:
1692                 return 0; // lowest
1693             case EXTERNAL:
1694                 return 1; // second lowest
1695             case LIMITED:
1696                 return 2;
1697             case FULL:
1698                 return 3; // good
1699             case LEVEL_3:
1700                 return 4;
1701             default:
1702                 fail("Unknown HW level: " + level);
1703         }
1704         return -1;
1705     }
1706 
toStringHardwareLevel(int level)1707     private static String toStringHardwareLevel(int level) {
1708         switch (level) {
1709             case LEGACY:
1710                 return "LEGACY";
1711             case LIMITED:
1712                 return "LIMITED";
1713             case FULL:
1714                 return "FULL";
1715             case EXTERNAL:
1716                 return "EXTERNAL";
1717             default:
1718                 if (level >= LEVEL_3) {
1719                     return String.format("LEVEL_%d", level);
1720                 }
1721         }
1722 
1723         // unknown
1724         Log.w(TAG, "Unknown hardware level " + level);
1725         return Integer.toString(level);
1726     }
1727 }
1728