• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.camera2.cts;
18 
19 import android.content.Context;
20 import android.graphics.ImageFormat;
21 import android.graphics.Rect;
22 import android.graphics.SurfaceTexture;
23 import android.hardware.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.cts.helpers.CameraErrorCollector;
28 import android.hardware.camera2.params.BlackLevelPattern;
29 import android.hardware.camera2.params.ColorSpaceTransform;
30 import android.hardware.camera2.params.StreamConfigurationMap;
31 import android.media.CamcorderProfile;
32 import android.media.ImageReader;
33 import android.test.AndroidTestCase;
34 import android.util.Log;
35 import android.util.Rational;
36 import android.util.Range;
37 import android.util.Size;
38 import android.view.Surface;
39 
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.List;
43 import java.util.Objects;
44 
45 import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
46 
47 /**
48  * Extended tests for static camera characteristics.
49  */
50 public class ExtendedCameraCharacteristicsTest extends AndroidTestCase {
51     private static final String TAG = "ExChrsTest"; // must be short so next line doesn't throw
52     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
53 
54     private static final String PREFIX_ANDROID = "android";
55     private static final String PREFIX_VENDOR = "com";
56 
57     /*
58      * Constants for static RAW metadata.
59      */
60     private static final int MIN_ALLOWABLE_WHITELEVEL = 32; // must have sensor bit depth > 5
61 
62     private CameraManager mCameraManager;
63     private List<CameraCharacteristics> mCharacteristics;
64     private String[] mIds;
65     private CameraErrorCollector mCollector;
66 
67     private static final Size FULLHD = new Size(1920, 1080);
68     private static final Size FULLHD_ALT = new Size(1920, 1088);
69     private static final Size HD = new Size(1280, 720);
70     private static final Size VGA = new Size(640, 480);
71     private static final Size QVGA = new Size(320, 240);
72 
73     /*
74      * HW Levels short hand
75      */
76     private static final int LEGACY = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
77     private static final int LIMITED = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
78     private static final int FULL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
79     private static final int OPT = Integer.MAX_VALUE;  // For keys that are optional on all hardware levels.
80 
81     /*
82      * Capabilities short hand
83      */
84     private static final int NONE = -1;
85     private static final int BC =
86             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
87     private static final int MANUAL_SENSOR =
88             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR;
89     private static final int MANUAL_POSTPROC =
90             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING;
91     private static final int RAW =
92             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW;
93     private static final int YUV_REPROCESS =
94             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING;
95     private static final int OPAQUE_REPROCESS =
96             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
97     private static final int CONSTRAINED_HIGH_SPEED =
98             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO;
99     private static final int HIGH_SPEED_FPS_LOWER_MIN = 30;
100     private static final int HIGH_SPEED_FPS_UPPER_MIN = 120;
101 
102     @Override
setContext(Context context)103     public void setContext(Context context) {
104         super.setContext(context);
105         mCameraManager = (CameraManager)context.getSystemService(Context.CAMERA_SERVICE);
106         assertNotNull("Can't connect to camera manager", mCameraManager);
107     }
108 
109     @Override
setUp()110     protected void setUp() throws Exception {
111         super.setUp();
112         mIds = mCameraManager.getCameraIdList();
113         mCharacteristics = new ArrayList<>();
114         mCollector = new CameraErrorCollector();
115         for (int i = 0; i < mIds.length; i++) {
116             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(mIds[i]);
117             assertNotNull(String.format("Can't get camera characteristics from: ID %s", mIds[i]),
118                     props);
119             mCharacteristics.add(props);
120         }
121     }
122 
123     @Override
tearDown()124     protected void tearDown() throws Exception {
125         mCharacteristics = null;
126 
127         try {
128             mCollector.verify();
129         } catch (Throwable e) {
130             // When new Exception(e) is used, exception info will be printed twice.
131             throw new Exception(e.getMessage());
132         } finally {
133             super.tearDown();
134         }
135     }
136 
137     /**
138      * Test that the available stream configurations contain a few required formats and sizes.
139      */
testAvailableStreamConfigs()140     public void testAvailableStreamConfigs() {
141         int counter = 0;
142         for (CameraCharacteristics c : mCharacteristics) {
143             StreamConfigurationMap config =
144                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
145             assertNotNull(String.format("No stream configuration map found for: ID %s",
146                     mIds[counter]), config);
147             int[] outputFormats = config.getOutputFormats();
148 
149             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
150             assertNotNull("android.request.availableCapabilities must never be null",
151                     actualCapabilities);
152 
153             // Check required formats exist (JPEG, and YUV_420_888).
154             if (!arrayContains(actualCapabilities, BC)) {
155                 Log.i(TAG, "Camera " + mIds[counter] +
156                     ": BACKWARD_COMPATIBLE capability not supported, skipping test");
157                 continue;
158             }
159 
160             assertArrayContains(
161                     String.format("No valid YUV_420_888 preview formats found for: ID %s",
162                             mIds[counter]), outputFormats, ImageFormat.YUV_420_888);
163             assertArrayContains(String.format("No JPEG image format for: ID %s",
164                     mIds[counter]), outputFormats, ImageFormat.JPEG);
165 
166             Size[] yuvSizes = config.getOutputSizes(ImageFormat.YUV_420_888);
167             Size[] jpegSizes = config.getOutputSizes(ImageFormat.JPEG);
168             Size[] privateSizes = config.getOutputSizes(ImageFormat.PRIVATE);
169 
170             CameraTestUtils.assertArrayNotEmpty(yuvSizes,
171                     String.format("No sizes for preview format %x for: ID %s",
172                             ImageFormat.YUV_420_888, mIds[counter]));
173 
174             Rect activeRect = CameraTestUtils.getValueNotNull(
175                     c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
176             Size activeArraySize = new Size(activeRect.width(), activeRect.height());
177             Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
178 
179             if (activeArraySize.getWidth() >= FULLHD.getWidth() &&
180                     activeArraySize.getHeight() >= FULLHD.getHeight()) {
181                 assertArrayContainsAnyOf(String.format(
182                         "Required FULLHD size not found for format %x for: ID %s",
183                         ImageFormat.JPEG, mIds[counter]), jpegSizes,
184                         new Size[] {FULLHD, FULLHD_ALT});
185             }
186 
187             if (activeArraySize.getWidth() >= HD.getWidth() &&
188                     activeArraySize.getHeight() >= HD.getHeight()) {
189                 assertArrayContains(String.format(
190                         "Required HD size not found for format %x for: ID %s",
191                         ImageFormat.JPEG, mIds[counter]), jpegSizes, HD);
192             }
193 
194             if (activeArraySize.getWidth() >= VGA.getWidth() &&
195                     activeArraySize.getHeight() >= VGA.getHeight()) {
196                 assertArrayContains(String.format(
197                         "Required VGA size not found for format %x for: ID %s",
198                         ImageFormat.JPEG, mIds[counter]), jpegSizes, VGA);
199             }
200 
201             if (activeArraySize.getWidth() >= QVGA.getWidth() &&
202                     activeArraySize.getHeight() >= QVGA.getHeight()) {
203                 assertArrayContains(String.format(
204                         "Required QVGA size not found for format %x for: ID %s",
205                         ImageFormat.JPEG, mIds[counter]), jpegSizes, QVGA);
206             }
207 
208             ArrayList<Size> jpegSizesList = new ArrayList<>(Arrays.asList(jpegSizes));
209             ArrayList<Size> yuvSizesList = new ArrayList<>(Arrays.asList(yuvSizes));
210             ArrayList<Size> privateSizesList = new ArrayList<>(Arrays.asList(privateSizes));
211 
212             CamcorderProfile maxVideoProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
213             Size maxVideoSize = new Size(
214                     maxVideoProfile.videoFrameWidth, maxVideoProfile.videoFrameHeight);
215 
216             // Handle FullHD special case first
217             if (jpegSizesList.contains(FULLHD)) {
218                 if (hwLevel == FULL || (hwLevel == LIMITED &&
219                         maxVideoSize.getWidth() >= FULLHD.getWidth() &&
220                         maxVideoSize.getHeight() >= FULLHD.getHeight())) {
221                     boolean yuvSupportFullHD = yuvSizesList.contains(FULLHD) ||
222                             yuvSizesList.contains(FULLHD_ALT);
223                     boolean privateSupportFullHD = privateSizesList.contains(FULLHD) ||
224                             privateSizesList.contains(FULLHD_ALT);
225                     assertTrue("Full device FullHD YUV size not found", yuvSupportFullHD);
226                     assertTrue("Full device FullHD PRIVATE size not found", privateSupportFullHD);
227                 }
228                 // remove all FullHD or FullHD_Alt sizes for the remaining of the test
229                 jpegSizesList.remove(FULLHD);
230                 jpegSizesList.remove(FULLHD_ALT);
231             }
232 
233             // Check all sizes other than FullHD
234             if (hwLevel == LIMITED) {
235                 // Remove all jpeg sizes larger than max video size
236                 ArrayList<Size> toBeRemoved = new ArrayList<>();
237                 for (Size size : jpegSizesList) {
238                     if (size.getWidth() >= maxVideoSize.getWidth() &&
239                             size.getHeight() >= maxVideoSize.getHeight()) {
240                         toBeRemoved.add(size);
241                     }
242                 }
243                 jpegSizesList.removeAll(toBeRemoved);
244             }
245 
246             if (hwLevel == FULL || hwLevel == LIMITED) {
247                 if (!yuvSizesList.containsAll(jpegSizesList)) {
248                     for (Size s : jpegSizesList) {
249                         if (!yuvSizesList.contains(s)) {
250                             fail("Size " + s + " not found in YUV format");
251                         }
252                     }
253                 }
254             }
255 
256             if (!privateSizesList.containsAll(yuvSizesList)) {
257                 for (Size s : yuvSizesList) {
258                     if (!privateSizesList.contains(s)) {
259                         fail("Size " + s + " not found in PRIVATE format");
260                     }
261                 }
262             }
263 
264             counter++;
265         }
266     }
267 
268     /**
269      * Test {@link CameraCharacteristics#getKeys}
270      */
testKeys()271     public void testKeys() {
272         int counter = 0;
273         for (CameraCharacteristics c : mCharacteristics) {
274             mCollector.setCameraId(mIds[counter]);
275 
276             if (VERBOSE) {
277                 Log.v(TAG, "testKeys - testing characteristics for camera " + mIds[counter]);
278             }
279 
280             List<CameraCharacteristics.Key<?>> allKeys = c.getKeys();
281             assertNotNull("Camera characteristics keys must not be null", allKeys);
282             assertFalse("Camera characteristics keys must have at least 1 key",
283                     allKeys.isEmpty());
284 
285             for (CameraCharacteristics.Key<?> key : allKeys) {
286                 assertKeyPrefixValid(key.getName());
287 
288                 // All characteristics keys listed must never be null
289                 mCollector.expectKeyValueNotNull(c, key);
290 
291                 // TODO: add a check that key must not be @hide
292             }
293 
294             /*
295              * List of keys that must be present in camera characteristics (not null).
296              *
297              * Keys for LIMITED, FULL devices might be available despite lacking either
298              * the hardware level or the capability. This is *OK*. This only lists the
299              * *minimal* requirements for a key to be listed.
300              *
301              * LEGACY devices are a bit special since they map to api1 devices, so we know
302              * for a fact most keys are going to be illegal there so they should never be
303              * available.
304              *
305              * For LIMITED-level keys, if the level is >= LIMITED, then the capabilities are used to
306              * do the actual checking.
307              */
308             {
309                 //                                           (Key Name)                                     (HW Level)  (Capabilities <Var-Arg>)
310                 expectKeyAvailable(c, CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES     , OPT      ,   BC                   );
311                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_MODES                         , OPT      ,   BC                   );
312                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES          , OPT      ,   BC                   );
313                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES                      , OPT      ,   BC                   );
314                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES          , OPT      ,   BC                   );
315                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE                   , OPT      ,   BC                   );
316                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP                    , OPT      ,   BC                   );
317                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE                       , OPT      ,   BC                   );
318                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES                      , OPT      ,   BC                   );
319                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS                       , OPT      ,   BC                   );
320                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES                   , OPT      ,   BC                   );
321                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES     , OPT      ,   BC                   );
322                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES                     , OPT      ,   BC                   );
323                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE                      , OPT      ,   BC                   );
324                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AE                          , OPT      ,   BC                   );
325                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AF                          , OPT      ,   BC                   );
326                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB                         , OPT      ,   BC                   );
327                 expectKeyAvailable(c, CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES                       , FULL     ,   NONE                 );
328                 expectKeyAvailable(c, CameraCharacteristics.FLASH_INFO_AVAILABLE                            , OPT      ,   BC                   );
329                 expectKeyAvailable(c, CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES             , OPT      ,   RAW                  );
330                 expectKeyAvailable(c, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL                   , OPT      ,   BC                   );
331                 expectKeyAvailable(c, CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES                  , OPT      ,   BC                   );
332                 expectKeyAvailable(c, CameraCharacteristics.LENS_FACING                                     , OPT      ,   BC                   );
333                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES                   , FULL     ,   MANUAL_SENSOR        );
334                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES            , FULL     ,   MANUAL_SENSOR        );
335                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS               , OPT      ,   BC                   );
336                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION       , LIMITED  ,   BC                   );
337                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION            , LIMITED  ,   MANUAL_SENSOR        );
338                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE                   , LIMITED  ,   BC                   );
339                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE                , LIMITED  ,   BC                   );
340                 expectKeyAvailable(c, CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES , OPT      ,   BC                   );
341                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES                  , OPT      ,   BC                   );
342                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS                   , OPT      ,   YUV_REPROCESS, OPAQUE_REPROCESS);
343                 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP                 , OPT      ,   CONSTRAINED_HIGH_SPEED);
344                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC                     , OPT      ,   BC                   );
345                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING            , OPT      ,   BC                   );
346                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW                      , OPT      ,   BC                   );
347                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT                    , OPT      ,   BC                   );
348                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH                      , OPT      ,   BC                   );
349                 expectKeyAvailable(c, CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM               , OPT      ,   BC                   );
350                 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP                 , OPT      ,   BC                   );
351                 expectKeyAvailable(c, CameraCharacteristics.SCALER_CROPPING_TYPE                            , OPT      ,   BC                   );
352                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES             , OPT      ,   BC                   );
353                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN                      , FULL     ,   MANUAL_SENSOR, RAW   );
354                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1                   , OPT      ,   RAW                  );
355                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM1                         , OPT      ,   RAW                  );
356                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX1                          , OPT      ,   RAW                  );
357                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE                   , OPT      ,   BC, RAW              );
358                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT            , FULL     ,   RAW                  );
359                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE                 , FULL     ,   MANUAL_SENSOR        );
360                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION                  , FULL     ,   MANUAL_SENSOR        );
361                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE                       , OPT      ,   BC                   );
362                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE                    , OPT      ,   BC                   );
363                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE                   , FULL     ,   MANUAL_SENSOR        );
364                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL                         , OPT      ,   RAW                  );
365                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE                    , OPT      ,   BC                   );
366                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY                   , FULL     ,   MANUAL_SENSOR        );
367                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_ORIENTATION                              , OPT      ,   BC                   );
368                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1                    , OPT      ,   RAW                  );
369                 expectKeyAvailable(c, CameraCharacteristics.SHADING_AVAILABLE_MODES                         , LIMITED  ,   MANUAL_POSTPROC, RAW );
370                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES     , OPT      ,   BC                   );
371                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES   , OPT      ,   RAW                  );
372                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES, LIMITED  ,   RAW                  );
373                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT                  , OPT      ,   BC                   );
374                 expectKeyAvailable(c, CameraCharacteristics.SYNC_MAX_LATENCY                                , OPT      ,   BC                   );
375                 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES                , FULL     ,   MANUAL_POSTPROC      );
376                 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS                        , FULL     ,   MANUAL_POSTPROC      );
377 
378                 // Future: Use column editors for modifying above, ignore line length to keep 1 key per line
379 
380                 // TODO: check that no other 'android' keys are listed in #getKeys if they aren't in the above list
381             }
382 
383             // Only check for these if the second reference illuminant is included
384             if (allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2)) {
385                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2                    , OPT      ,   RAW                  );
386                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM2                         , OPT      ,   RAW                  );
387                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2                   , OPT      ,   RAW                  );
388                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX2                          , OPT      ,   RAW                  );
389             }
390 
391             counter++;
392         }
393     }
394 
395     /**
396      * Test values for static metadata used by the RAW capability.
397      */
testStaticRawCharacteristics()398     public void testStaticRawCharacteristics() {
399         int counter = 0;
400         for (CameraCharacteristics c : mCharacteristics) {
401             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
402             assertNotNull("android.request.availableCapabilities must never be null",
403                     actualCapabilities);
404             if (!arrayContains(actualCapabilities,
405                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
406                 Log.i(TAG, "RAW capability is not supported in camera " + counter++ +
407                         ". Skip the test.");
408                 continue;
409             }
410 
411             Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
412             if (actualHwLevel != null && actualHwLevel == FULL) {
413                 mCollector.expectKeyValueContains(c,
414                         CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES,
415                         CameraCharacteristics.HOT_PIXEL_MODE_FAST);
416             }
417             mCollector.expectKeyValueContains(c,
418                     CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES, false);
419             mCollector.expectKeyValueGreaterThan(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL,
420                     MIN_ALLOWABLE_WHITELEVEL);
421 
422             mCollector.expectKeyValueIsIn(c,
423                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
424                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB,
425                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG,
426                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG,
427                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR);
428             // TODO: SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB isn't supported yet.
429 
430             mCollector.expectKeyValueInRange(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1,
431                     CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT,
432                     CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN);
433             mCollector.expectKeyValueInRange(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2,
434                     (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT,
435                     (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN);
436 
437             Rational[] zeroes = new Rational[9];
438             Arrays.fill(zeroes, Rational.ZERO);
439 
440             ColorSpaceTransform zeroed = new ColorSpaceTransform(zeroes);
441             mCollector.expectNotEquals("Forward Matrix1 should not contain all zeroes.", zeroed,
442                     c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX1));
443             mCollector.expectNotEquals("Forward Matrix2 should not contain all zeroes.", zeroed,
444                     c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX2));
445             mCollector.expectNotEquals("Calibration Transform1 should not contain all zeroes.",
446                     zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1));
447             mCollector.expectNotEquals("Calibration Transform2 should not contain all zeroes.",
448                     zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2));
449             mCollector.expectNotEquals("Color Transform1 should not contain all zeroes.",
450                     zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1));
451             mCollector.expectNotEquals("Color Transform2 should not contain all zeroes.",
452                     zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2));
453 
454             BlackLevelPattern blackLevel = mCollector.expectKeyValueNotNull(c,
455                     CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN);
456             if (blackLevel != null) {
457                 String blackLevelPatternString = blackLevel.toString();
458                 if (VERBOSE) {
459                     Log.v(TAG, "Black level pattern: " + blackLevelPatternString);
460                 }
461                 int[] blackLevelPattern = new int[BlackLevelPattern.COUNT];
462                 blackLevel.copyTo(blackLevelPattern, /*offset*/0);
463                 Integer whitelevel = c.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
464                 if (whitelevel != null) {
465                     mCollector.expectValuesInRange("BlackLevelPattern", blackLevelPattern, 0,
466                             whitelevel);
467                 } else {
468                     mCollector.addMessage(
469                             "No WhiteLevel available, cannot check BlackLevelPattern range.");
470                 }
471             }
472 
473             // TODO: profileHueSatMap, and profileToneCurve aren't supported yet.
474             counter++;
475         }
476     }
477 
478     /**
479      * Test values for static metadata used by the BURST capability.
480      */
testStaticBurstCharacteristics()481     public void testStaticBurstCharacteristics() throws Exception {
482         int counter = 0;
483         final float SIZE_ERROR_MARGIN = 0.03f;
484         for (CameraCharacteristics c : mCharacteristics) {
485             int[] actualCapabilities = CameraTestUtils.getValueNotNull(
486                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
487 
488             // Check if the burst capability is defined
489             boolean haveBurstCapability = arrayContains(actualCapabilities,
490                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
491             boolean haveBC = arrayContains(actualCapabilities,
492                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
493 
494             if(haveBurstCapability && !haveBC) {
495                 fail("Must have BACKWARD_COMPATIBLE capability if BURST_CAPTURE capability is defined");
496             }
497 
498             if (!haveBC) continue;
499 
500             StreamConfigurationMap config =
501                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
502             assertNotNull(String.format("No stream configuration map found for: ID %s",
503                     mIds[counter]), config);
504             Rect activeRect = CameraTestUtils.getValueNotNull(
505                     c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
506             Size sensorSize = new Size(activeRect.width(), activeRect.height());
507 
508             // Ensure that max YUV size matches max JPEG size
509             Size maxYuvSize = CameraTestUtils.getMaxSize(
510                     config.getOutputSizes(ImageFormat.YUV_420_888));
511             Size maxFastYuvSize = maxYuvSize;
512 
513             Size[] slowYuvSizes = config.getHighResolutionOutputSizes(ImageFormat.YUV_420_888);
514             if (haveBurstCapability && slowYuvSizes != null && slowYuvSizes.length > 0) {
515                 Size maxSlowYuvSize = CameraTestUtils.getMaxSize(slowYuvSizes);
516                 maxYuvSize = CameraTestUtils.getMaxSize(new Size[]{maxYuvSize, maxSlowYuvSize});
517             }
518 
519             Size maxJpegSize = CameraTestUtils.getMaxSize(CameraTestUtils.getSupportedSizeForFormat(
520                     ImageFormat.JPEG, mIds[counter], mCameraManager));
521 
522             boolean haveMaxYuv = maxYuvSize != null ?
523                 (maxJpegSize.getWidth() <= maxYuvSize.getWidth() &&
524                         maxJpegSize.getHeight() <= maxYuvSize.getHeight()) : false;
525 
526             boolean maxYuvMatchSensor =
527                     (maxYuvSize.getWidth() <= sensorSize.getWidth() * (1.0 + SIZE_ERROR_MARGIN) &&
528                      maxYuvSize.getWidth() >= sensorSize.getWidth() * (1.0 - SIZE_ERROR_MARGIN) &&
529                      maxYuvSize.getHeight() <= sensorSize.getHeight() * (1.0 + SIZE_ERROR_MARGIN) &&
530                      maxYuvSize.getHeight() >= sensorSize.getHeight() * (1.0 - SIZE_ERROR_MARGIN));
531 
532             // No need to do null check since framework will generate the key if HAL don't supply
533             boolean haveAeLock = CameraTestUtils.getValueNotNull(
534                     c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE);
535             boolean haveAwbLock = CameraTestUtils.getValueNotNull(
536                     c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE);
537 
538             // Ensure that max YUV output is fast enough - needs to be at least 10 fps
539 
540             long maxYuvRate =
541                 config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxYuvSize);
542             final long MIN_MAXSIZE_DURATION_BOUND_NS = 100000000; // 100 ms, 10 fps
543             boolean haveMaxYuvRate = maxYuvRate <= MIN_MAXSIZE_DURATION_BOUND_NS;
544 
545             // Ensure that some >=8MP YUV output is fast enough - needs to be at least 20 fps
546 
547             long maxFastYuvRate =
548                     config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxFastYuvSize);
549             final long MIN_8MP_DURATION_BOUND_NS = 200000000; // 50 ms, 20 fps
550             boolean haveFastYuvRate = maxFastYuvRate <= MIN_8MP_DURATION_BOUND_NS;
551 
552             final int SIZE_8MP_BOUND = 8000000;
553             boolean havefast8MPYuv = (maxFastYuvSize.getWidth() * maxFastYuvSize.getHeight()) >
554                     SIZE_8MP_BOUND;
555 
556             // Ensure that there's an FPS range that's fast enough to capture at above
557             // minFrameDuration, for full-auto bursts at the fast resolutions
558             Range[] fpsRanges = CameraTestUtils.getValueNotNull(
559                     c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
560             float minYuvFps = 1.f / maxFastYuvRate;
561 
562             boolean haveFastAeTargetFps = false;
563             for (Range<Integer> r : fpsRanges) {
564                 if (r.getLower() >= minYuvFps) {
565                     haveFastAeTargetFps = true;
566                     break;
567                 }
568             }
569 
570             // Ensure that maximum sync latency is small enough for fast setting changes, even if
571             // it's not quite per-frame
572 
573             Integer maxSyncLatencyValue = c.get(CameraCharacteristics.SYNC_MAX_LATENCY);
574             assertNotNull(String.format("No sync latency declared for ID %s", mIds[counter]),
575                     maxSyncLatencyValue);
576 
577             int maxSyncLatency = maxSyncLatencyValue;
578             final long MAX_LATENCY_BOUND = 4;
579             boolean haveFastSyncLatency =
580                 (maxSyncLatency <= MAX_LATENCY_BOUND) && (maxSyncLatency >= 0);
581 
582             if (haveBurstCapability) {
583                 assertTrue("Must have slow YUV size array when BURST_CAPTURE capability is defined!",
584                         slowYuvSizes != null);
585                 assertTrue(
586                         String.format("BURST-capable camera device %s does not have maximum YUV " +
587                                 "size that is at least max JPEG size",
588                                 mIds[counter]),
589                         haveMaxYuv);
590                 assertTrue(
591                         String.format("BURST-capable camera device %s max-resolution " +
592                                 "YUV frame rate is too slow" +
593                                 "(%d ns min frame duration reported, less than %d ns expected)",
594                                 mIds[counter], maxYuvRate, MIN_MAXSIZE_DURATION_BOUND_NS),
595                         haveMaxYuvRate);
596                 assertTrue(
597                         String.format("BURST-capable camera device %s >= 8MP YUV output " +
598                                 "frame rate is too slow" +
599                                 "(%d ns min frame duration reported, less than %d ns expected)",
600                                 mIds[counter], maxYuvRate, MIN_8MP_DURATION_BOUND_NS),
601                         haveFastYuvRate);
602                 assertTrue(
603                         String.format("BURST-capable camera device %s does not list an AE target " +
604                                 " FPS range with min FPS >= %f, for full-AUTO bursts",
605                                 mIds[counter], minYuvFps),
606                         haveFastAeTargetFps);
607                 assertTrue(
608                         String.format("BURST-capable camera device %s YUV sync latency is too long" +
609                                 "(%d frames reported, [0, %d] frames expected)",
610                                 mIds[counter], maxSyncLatency, MAX_LATENCY_BOUND),
611                         haveFastSyncLatency);
612                 assertTrue(
613                         String.format("BURST-capable camera device %s max YUV size %s should be" +
614                                 "close to active array size %s",
615                                 mIds[counter], maxYuvSize.toString(), sensorSize.toString()),
616                         maxYuvMatchSensor);
617                 assertTrue(
618                         String.format("BURST-capable camera device %s does not support AE lock",
619                                 mIds[counter]),
620                         haveAeLock);
621                 assertTrue(
622                         String.format("BURST-capable camera device %s does not support AWB lock",
623                                 mIds[counter]),
624                         haveAwbLock);
625             } else {
626                 assertTrue("Must have null slow YUV size array when no BURST_CAPTURE capability!",
627                         slowYuvSizes == null);
628                 assertTrue(
629                         String.format("Camera device %s has all the requirements for BURST" +
630                                 " capability but does not report it!", mIds[counter]),
631                         !(haveMaxYuv && haveMaxYuvRate && haveFastAeTargetFps &&
632                                 haveFastSyncLatency && maxYuvMatchSensor &&
633                                 haveAeLock && haveAwbLock));
634             }
635 
636             counter++;
637         }
638     }
639 
640     /**
641      * Check reprocessing capabilities.
642      */
testReprocessingCharacteristics()643     public void testReprocessingCharacteristics() {
644         int counter = 0;
645 
646         for (CameraCharacteristics c : mCharacteristics) {
647             Log.i(TAG, "testReprocessingCharacteristics: Testing camera ID " + mIds[counter]);
648 
649             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
650             assertNotNull("android.request.availableCapabilities must never be null",
651                     capabilities);
652             boolean supportYUV = arrayContains(capabilities,
653                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
654             boolean supportOpaque = arrayContains(capabilities,
655                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
656             StreamConfigurationMap configs =
657                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
658             Integer maxNumInputStreams =
659                     c.get(CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS);
660             int[] availableEdgeModes = c.get(CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES);
661             int[] availableNoiseReductionModes = c.get(
662                     CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES);
663 
664             int[] inputFormats = configs.getInputFormats();
665 
666             boolean supportZslEdgeMode = false;
667             boolean supportZslNoiseReductionMode = false;
668 
669             if (availableEdgeModes != null) {
670                 supportZslEdgeMode = Arrays.asList(CameraTestUtils.toObject(availableEdgeModes)).
671                         contains(CaptureRequest.EDGE_MODE_ZERO_SHUTTER_LAG);
672             }
673 
674             if (availableNoiseReductionModes != null) {
675                 supportZslNoiseReductionMode = Arrays.asList(
676                         CameraTestUtils.toObject(availableNoiseReductionModes)).contains(
677                         CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG);
678             }
679 
680             if (supportYUV || supportOpaque) {
681                 mCollector.expectTrue("Support reprocessing but max number of input stream is " +
682                         maxNumInputStreams, maxNumInputStreams != null && maxNumInputStreams > 0);
683                 mCollector.expectTrue("Support reprocessing but EDGE_MODE_ZERO_SHUTTER_LAG is " +
684                         "not supported", supportZslEdgeMode);
685                 mCollector.expectTrue("Support reprocessing but " +
686                         "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is not supported",
687                         supportZslNoiseReductionMode);
688 
689                 // Verify mandatory input formats are supported
690                 mCollector.expectTrue("YUV_420_888 input must be supported for YUV reprocessing",
691                         !supportYUV || arrayContains(inputFormats, ImageFormat.YUV_420_888));
692                 mCollector.expectTrue("PRIVATE input must be supported for OPAQUE reprocessing",
693                         !supportOpaque || arrayContains(inputFormats, ImageFormat.PRIVATE));
694 
695                 // max capture stall must be reported if one of the reprocessing is supported.
696                 final int MAX_ALLOWED_STALL_FRAMES = 4;
697                 Integer maxCaptureStall = c.get(CameraCharacteristics.REPROCESS_MAX_CAPTURE_STALL);
698                 mCollector.expectTrue("max capture stall must be non-null and no larger than "
699                         + MAX_ALLOWED_STALL_FRAMES,
700                         maxCaptureStall != null && maxCaptureStall <= MAX_ALLOWED_STALL_FRAMES);
701 
702                 for (int input : inputFormats) {
703                     // Verify mandatory output formats are supported
704                     int[] outputFormats = configs.getValidOutputFormatsForInput(input);
705                     mCollector.expectTrue("YUV_420_888 output must be supported for reprocessing",
706                             arrayContains(outputFormats, ImageFormat.YUV_420_888));
707                     mCollector.expectTrue("JPEG output must be supported for reprocessing",
708                             arrayContains(outputFormats, ImageFormat.JPEG));
709 
710                     // Verify camera can output the reprocess input formats and sizes.
711                     Size[] inputSizes = configs.getInputSizes(input);
712                     Size[] outputSizes = configs.getOutputSizes(input);
713                     Size[] highResOutputSizes = configs.getHighResolutionOutputSizes(input);
714                     mCollector.expectTrue("no input size supported for format " + input,
715                             inputSizes.length > 0);
716                     mCollector.expectTrue("no output size supported for format " + input,
717                             outputSizes.length > 0);
718 
719                     for (Size inputSize : inputSizes) {
720                         mCollector.expectTrue("Camera must be able to output the supported " +
721                                 "reprocessing input size",
722                                 arrayContains(outputSizes, inputSize) ||
723                                 arrayContains(highResOutputSizes, inputSize));
724                     }
725                 }
726             } else {
727                 mCollector.expectTrue("Doesn't support reprocessing but report input format: " +
728                         Arrays.toString(inputFormats), inputFormats.length == 0);
729                 mCollector.expectTrue("Doesn't support reprocessing but max number of input " +
730                         "stream is " + maxNumInputStreams,
731                         maxNumInputStreams == null || maxNumInputStreams == 0);
732                 mCollector.expectTrue("Doesn't support reprocessing but " +
733                         "EDGE_MODE_ZERO_SHUTTER_LAG is supported", !supportZslEdgeMode);
734                 mCollector.expectTrue("Doesn't support reprocessing but " +
735                         "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is supported",
736                         !supportZslNoiseReductionMode);
737             }
738         }
739     }
740 
741     /**
742      * Check depth output capability
743      */
testDepthOutputCharacteristics()744     public void testDepthOutputCharacteristics() {
745         int counter = 0;
746 
747         for (CameraCharacteristics c : mCharacteristics) {
748             Log.i(TAG, "testDepthOutputCharacteristics: Testing camera ID " + mIds[counter]);
749 
750             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
751             assertNotNull("android.request.availableCapabilities must never be null",
752                     capabilities);
753             boolean supportDepth = arrayContains(capabilities,
754                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT);
755             StreamConfigurationMap configs =
756                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
757 
758             int[] outputFormats = configs.getOutputFormats();
759             boolean hasDepth16 = arrayContains(outputFormats, ImageFormat.DEPTH16);
760 
761             Boolean depthIsExclusive = c.get(CameraCharacteristics.DEPTH_DEPTH_IS_EXCLUSIVE);
762 
763             float[] poseRotation = c.get(CameraCharacteristics.LENS_POSE_ROTATION);
764             float[] poseTranslation = c.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
765             float[] cameraIntrinsics = c.get(CameraCharacteristics.LENS_INTRINSIC_CALIBRATION);
766             float[] radialDistortion = c.get(CameraCharacteristics.LENS_RADIAL_DISTORTION);
767 
768             if (supportDepth) {
769                 mCollector.expectTrue("Supports DEPTH_OUTPUT but does not support DEPTH16",
770                         hasDepth16);
771                 if (hasDepth16) {
772                     Size[] depthSizes = configs.getOutputSizes(ImageFormat.DEPTH16);
773                     mCollector.expectTrue("Supports DEPTH_OUTPUT but no sizes for DEPTH16 supported!",
774                             depthSizes != null && depthSizes.length > 0);
775                     if (depthSizes != null) {
776                         for (Size depthSize : depthSizes) {
777                             mCollector.expectTrue("All depth16 sizes must be positive",
778                                     depthSize.getWidth() > 0 && depthSize.getHeight() > 0);
779                             long minFrameDuration = configs.getOutputMinFrameDuration(
780                                     ImageFormat.DEPTH16, depthSize);
781                             mCollector.expectTrue("Non-negative min frame duration for depth size "
782                                     + depthSize + " expected, got " + minFrameDuration,
783                                     minFrameDuration >= 0);
784                             long stallDuration = configs.getOutputStallDuration(
785                                     ImageFormat.DEPTH16, depthSize);
786                             mCollector.expectTrue("Non-negative stall duration for depth size "
787                                     + depthSize + " expected, got " + stallDuration,
788                                     stallDuration >= 0);
789                         }
790                     }
791                 }
792                 if (arrayContains(outputFormats, ImageFormat.DEPTH_POINT_CLOUD)) {
793                     Size[] depthCloudSizes = configs.getOutputSizes(ImageFormat.DEPTH_POINT_CLOUD);
794                     mCollector.expectTrue("Supports DEPTH_POINT_CLOUD " +
795                             "but no sizes for DEPTH_POINT_CLOUD supported!",
796                             depthCloudSizes != null && depthCloudSizes.length > 0);
797                     if (depthCloudSizes != null) {
798                         for (Size depthCloudSize : depthCloudSizes) {
799                             mCollector.expectTrue("All depth point cloud sizes must be nonzero",
800                                     depthCloudSize.getWidth() > 0);
801                             mCollector.expectTrue("All depth point cloud sizes must be N x 1",
802                                     depthCloudSize.getHeight() == 1);
803                             long minFrameDuration = configs.getOutputMinFrameDuration(
804                                     ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
805                             mCollector.expectTrue("Non-negative min frame duration for depth size "
806                                     + depthCloudSize + " expected, got " + minFrameDuration,
807                                     minFrameDuration >= 0);
808                             long stallDuration = configs.getOutputStallDuration(
809                                     ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
810                             mCollector.expectTrue("Non-negative stall duration for depth size "
811                                     + depthCloudSize + " expected, got " + stallDuration,
812                                     stallDuration >= 0);
813                         }
814                     }
815                 }
816 
817                 mCollector.expectTrue("Supports DEPTH_OUTPUT but DEPTH_IS_EXCLUSIVE is not defined",
818                         depthIsExclusive != null);
819 
820                 mCollector.expectTrue(
821                         "Supports DEPTH_OUTPUT but LENS_POSE_ROTATION not right size",
822                         poseRotation != null && poseRotation.length == 4);
823                 mCollector.expectTrue(
824                         "Supports DEPTH_OUTPUT but LENS_POSE_TRANSLATION not right size",
825                         poseTranslation != null && poseTranslation.length == 3);
826                 mCollector.expectTrue(
827                         "Supports DEPTH_OUTPUT but LENS_INTRINSIC_CALIBRATION not right size",
828                         cameraIntrinsics != null && cameraIntrinsics.length == 5);
829                 mCollector.expectTrue(
830                         "Supports DEPTH_OUTPUT but LENS_RADIAL_DISTORTION not right size",
831                         radialDistortion != null && radialDistortion.length == 6);
832 
833                 if (poseRotation != null && poseRotation.length == 4) {
834                     float normSq =
835                         poseRotation[0] * poseRotation[0] +
836                         poseRotation[1] * poseRotation[1] +
837                         poseRotation[2] * poseRotation[2] +
838                         poseRotation[3] * poseRotation[3];
839                     mCollector.expectTrue(
840                             "LENS_POSE_ROTATION quarternion must be unit-length",
841                             0.9999f < normSq && normSq < 1.0001f);
842 
843                     // TODO: Cross-validate orientation/facing and poseRotation
844                     Integer orientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
845                     Integer facing = c.get(CameraCharacteristics.LENS_FACING);
846                 }
847 
848                 if (poseTranslation != null && poseTranslation.length == 3) {
849                     float normSq =
850                         poseTranslation[0] * poseTranslation[0] +
851                         poseTranslation[1] * poseTranslation[1] +
852                         poseTranslation[2] * poseTranslation[2];
853                     mCollector.expectTrue("Pose translation is larger than 1 m",
854                             normSq < 1.f);
855                 }
856 
857                 Rect precorrectionArray =
858                     c.get(CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
859                 mCollector.expectTrue("Supports DEPTH_OUTPUT but does not have " +
860                         "precorrection active array defined", precorrectionArray != null);
861 
862                 if (cameraIntrinsics != null && precorrectionArray != null) {
863                     float fx = cameraIntrinsics[0];
864                     float fy = cameraIntrinsics[1];
865                     float cx = cameraIntrinsics[2];
866                     float cy = cameraIntrinsics[3];
867                     float s = cameraIntrinsics[4];
868                     mCollector.expectTrue("Optical center expected to be within precorrection array",
869                             0 <= cx && cx < precorrectionArray.width() &&
870                             0 <= cy && cy < precorrectionArray.height());
871 
872                     // TODO: Verify focal lengths and skew are reasonable
873                 }
874 
875                 if (radialDistortion != null) {
876                     // TODO: Verify radial distortion
877                 }
878 
879             } else {
880                 boolean hasFields =
881                     hasDepth16 && (poseTranslation != null) &&
882                     (poseRotation != null) && (cameraIntrinsics != null) &&
883                     (radialDistortion != null) && (depthIsExclusive != null);
884 
885                 mCollector.expectTrue(
886                         "All necessary depth fields defined, but DEPTH_OUTPUT capability is not listed",
887                         !hasFields);
888             }
889         }
890     }
891 
892     /**
893      * Cross-check StreamConfigurationMap output
894      */
testStreamConfigurationMap()895     public void testStreamConfigurationMap() throws Exception {
896         int counter = 0;
897         for (CameraCharacteristics c : mCharacteristics) {
898             Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + mIds[counter]);
899             StreamConfigurationMap config =
900                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
901             assertNotNull(String.format("No stream configuration map found for: ID %s",
902                             mIds[counter]), config);
903 
904             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
905             assertNotNull("android.request.availableCapabilities must never be null",
906                     actualCapabilities);
907 
908             if (arrayContains(actualCapabilities, BC)) {
909                 assertTrue("ImageReader must be supported",
910                     config.isOutputSupportedFor(android.media.ImageReader.class));
911                 assertTrue("MediaRecorder must be supported",
912                     config.isOutputSupportedFor(android.media.MediaRecorder.class));
913                 assertTrue("MediaCodec must be supported",
914                     config.isOutputSupportedFor(android.media.MediaCodec.class));
915                 assertTrue("Allocation must be supported",
916                     config.isOutputSupportedFor(android.renderscript.Allocation.class));
917                 assertTrue("SurfaceHolder must be supported",
918                     config.isOutputSupportedFor(android.view.SurfaceHolder.class));
919                 assertTrue("SurfaceTexture must be supported",
920                     config.isOutputSupportedFor(android.graphics.SurfaceTexture.class));
921 
922                 assertTrue("YUV_420_888 must be supported",
923                     config.isOutputSupportedFor(ImageFormat.YUV_420_888));
924                 assertTrue("JPEG must be supported",
925                     config.isOutputSupportedFor(ImageFormat.JPEG));
926             } else {
927                 assertTrue("YUV_420_88 may not be supported if BACKWARD_COMPATIBLE capability is not listed",
928                     !config.isOutputSupportedFor(ImageFormat.YUV_420_888));
929                 assertTrue("JPEG may not be supported if BACKWARD_COMPATIBLE capability is not listed",
930                     !config.isOutputSupportedFor(ImageFormat.JPEG));
931             }
932 
933             // Legacy YUV formats should not be listed
934             assertTrue("NV21 must not be supported",
935                     !config.isOutputSupportedFor(ImageFormat.NV21));
936 
937             // Check RAW
938 
939             if (arrayContains(actualCapabilities,
940                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
941                 assertTrue("RAW_SENSOR must be supported if RAW capability is advertised",
942                     config.isOutputSupportedFor(ImageFormat.RAW_SENSOR));
943             }
944 
945             // Cross check public formats and sizes
946 
947             int[] supportedFormats = config.getOutputFormats();
948             for (int format : supportedFormats) {
949                 assertTrue("Format " + format + " fails cross check",
950                         config.isOutputSupportedFor(format));
951                 List<Size> supportedSizes = CameraTestUtils.getAscendingOrderSizes(
952                         Arrays.asList(config.getOutputSizes(format)), /*ascending*/true);
953                 if (arrayContains(actualCapabilities,
954                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
955                     supportedSizes.addAll(
956                         Arrays.asList(config.getHighResolutionOutputSizes(format)));
957                     supportedSizes = CameraTestUtils.getAscendingOrderSizes(
958                         supportedSizes, /*ascending*/true);
959                 }
960                 assertTrue("Supported format " + format + " has no sizes listed",
961                         supportedSizes.size() > 0);
962                 for (int i = 0; i < supportedSizes.size(); i++) {
963                     Size size = supportedSizes.get(i);
964                     if (VERBOSE) {
965                         Log.v(TAG,
966                                 String.format("Testing camera %s, format %d, size %s",
967                                         mIds[counter], format, size.toString()));
968                     }
969 
970                     long stallDuration = config.getOutputStallDuration(format, size);
971                     switch(format) {
972                         case ImageFormat.YUV_420_888:
973                             assertTrue("YUV_420_888 may not have a non-zero stall duration",
974                                     stallDuration == 0);
975                             break;
976                         case ImageFormat.JPEG:
977                         case ImageFormat.RAW_SENSOR:
978                             final float TOLERANCE_FACTOR = 2.0f;
979                             long prevDuration = 0;
980                             if (i > 0) {
981                                 prevDuration = config.getOutputStallDuration(
982                                         format, supportedSizes.get(i - 1));
983                             }
984                             long nextDuration = Long.MAX_VALUE;
985                             if (i < (supportedSizes.size() - 1)) {
986                                 nextDuration = config.getOutputStallDuration(
987                                         format, supportedSizes.get(i + 1));
988                             }
989                             long curStallDuration = config.getOutputStallDuration(format, size);
990                             // Stall duration should be in a reasonable range: larger size should
991                             // normally have larger stall duration.
992                             mCollector.expectInRange("Stall duration (format " + format +
993                                     " and size " + size + ") is not in the right range",
994                                     curStallDuration,
995                                     (long) (prevDuration / TOLERANCE_FACTOR),
996                                     (long) (nextDuration * TOLERANCE_FACTOR));
997                             break;
998                         default:
999                             assertTrue("Negative stall duration for format " + format,
1000                                     stallDuration >= 0);
1001                             break;
1002                     }
1003                     long minDuration = config.getOutputMinFrameDuration(format, size);
1004                     if (arrayContains(actualCapabilities,
1005                             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
1006                         assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
1007                                 + "format " + format + " for size " + size + " minDuration " +
1008                                 minDuration,
1009                                 minDuration > 0);
1010                     } else {
1011                         assertTrue("Need non-negative min frame duration for format " + format,
1012                                 minDuration >= 0);
1013                     }
1014 
1015                     // todo: test opaque image reader when it's supported.
1016                     if (format != ImageFormat.PRIVATE) {
1017                         ImageReader testReader = ImageReader.newInstance(
1018                             size.getWidth(),
1019                             size.getHeight(),
1020                             format,
1021                             1);
1022                         Surface testSurface = testReader.getSurface();
1023 
1024                         assertTrue(
1025                             String.format("isOutputSupportedFor fails for config %s, format %d",
1026                                     size.toString(), format),
1027                             config.isOutputSupportedFor(testSurface));
1028 
1029                         testReader.close();
1030                     }
1031                 } // sizes
1032 
1033                 // Try an invalid size in this format, should round
1034                 Size invalidSize = findInvalidSize(supportedSizes);
1035                 int MAX_ROUNDING_WIDTH = 1920;
1036                 // todo: test opaque image reader when it's supported.
1037                 if (format != ImageFormat.PRIVATE &&
1038                         invalidSize.getWidth() <= MAX_ROUNDING_WIDTH) {
1039                     ImageReader testReader = ImageReader.newInstance(
1040                                                                      invalidSize.getWidth(),
1041                                                                      invalidSize.getHeight(),
1042                                                                      format,
1043                                                                      1);
1044                     Surface testSurface = testReader.getSurface();
1045 
1046                     assertTrue(
1047                                String.format("isOutputSupportedFor fails for config %s, %d",
1048                                        invalidSize.toString(), format),
1049                                config.isOutputSupportedFor(testSurface));
1050 
1051                     testReader.close();
1052                 }
1053             } // formats
1054 
1055             // Cross-check opaque format and sizes
1056             if (arrayContains(actualCapabilities, BC)) {
1057                 SurfaceTexture st = new SurfaceTexture(1);
1058                 Surface surf = new Surface(st);
1059 
1060                 Size[] opaqueSizes = CameraTestUtils.getSupportedSizeForClass(SurfaceTexture.class,
1061                         mIds[counter], mCameraManager);
1062                 assertTrue("Opaque format has no sizes listed",
1063                         opaqueSizes.length > 0);
1064                 for (Size size : opaqueSizes) {
1065                     long stallDuration = config.getOutputStallDuration(SurfaceTexture.class, size);
1066                     assertTrue("Opaque output may not have a non-zero stall duration",
1067                             stallDuration == 0);
1068 
1069                     long minDuration = config.getOutputMinFrameDuration(SurfaceTexture.class, size);
1070                     if (arrayContains(actualCapabilities,
1071                                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
1072                         assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
1073                                 + "opaque format",
1074                                 minDuration > 0);
1075                     } else {
1076                         assertTrue("Need non-negative min frame duration for opaque format ",
1077                                 minDuration >= 0);
1078                     }
1079                     st.setDefaultBufferSize(size.getWidth(), size.getHeight());
1080 
1081                     assertTrue(
1082                             String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
1083                                     size.toString()),
1084                             config.isOutputSupportedFor(surf));
1085 
1086                 } // opaque sizes
1087 
1088                 // Try invalid opaque size, should get rounded
1089                 Size invalidSize = findInvalidSize(opaqueSizes);
1090                 st.setDefaultBufferSize(invalidSize.getWidth(), invalidSize.getHeight());
1091                 assertTrue(
1092                         String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
1093                                 invalidSize.toString()),
1094                         config.isOutputSupportedFor(surf));
1095 
1096                 counter++;
1097             }
1098 
1099         } // mCharacteristics
1100     }
1101 
1102     /**
1103      * Test high speed capability and cross-check the high speed sizes and fps ranges from
1104      * the StreamConfigurationMap.
1105      */
testConstrainedHighSpeedCapability()1106     public void testConstrainedHighSpeedCapability() throws Exception {
1107         int counter = 0;
1108         for (CameraCharacteristics c : mCharacteristics) {
1109             int[] capabilities = CameraTestUtils.getValueNotNull(
1110                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1111             boolean supportHighSpeed = arrayContains(capabilities, CONSTRAINED_HIGH_SPEED);
1112             if (supportHighSpeed) {
1113                 StreamConfigurationMap config =
1114                         CameraTestUtils.getValueNotNull(
1115                                 c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1116                 List<Size> highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes());
1117                 assertTrue("High speed sizes shouldn't be empty", highSpeedSizes.size() > 0);
1118                 Size[] allSizes = CameraTestUtils.getSupportedSizeForFormat(ImageFormat.PRIVATE,
1119                         mIds[counter], mCameraManager);
1120                 assertTrue("Normal size for PRIVATE format shouldn't be null or empty",
1121                         allSizes != null && allSizes.length > 0);
1122                 for (Size size: highSpeedSizes) {
1123                     // The sizes must be a subset of the normal sizes
1124                     assertTrue("High speed size " + size +
1125                             " must be part of normal sizes " + Arrays.toString(allSizes),
1126                             Arrays.asList(allSizes).contains(size));
1127 
1128                     // Sanitize the high speed FPS ranges for each size
1129                     List<Range<Integer>> ranges =
1130                             Arrays.asList(config.getHighSpeedVideoFpsRangesFor(size));
1131                     for (Range<Integer> range : ranges) {
1132                         assertTrue("The range " + range + " doesn't satisfy the"
1133                                 + " min/max boundary requirements.",
1134                                 range.getLower() >= HIGH_SPEED_FPS_LOWER_MIN &&
1135                                 range.getUpper() >= HIGH_SPEED_FPS_UPPER_MIN);
1136                         assertTrue("The range " + range + " should be multiple of 30fps",
1137                                 range.getLower() % 30 == 0 && range.getUpper() % 30 == 0);
1138                         // If the range is fixed high speed range, it should contain the
1139                         // [30, fps_max] in the high speed range list; if it's variable FPS range,
1140                         // the corresponding fixed FPS Range must be included in the range list.
1141                         if (range.getLower() == range.getUpper()) {
1142                             Range<Integer> variableRange = new Range<Integer>(30, range.getUpper());
1143                             assertTrue("The variable FPS range " + variableRange +
1144                                     " shoould be included in the high speed ranges for size " +
1145                                     size, ranges.contains(variableRange));
1146                         } else {
1147                             Range<Integer> fixedRange =
1148                                     new Range<Integer>(range.getUpper(), range.getUpper());
1149                             assertTrue("The fixed FPS range " + fixedRange +
1150                                     " shoould be included in the high speed ranges for size " +
1151                                     size, ranges.contains(fixedRange));
1152                         }
1153                     }
1154                 }
1155                 // If the device advertise some high speed profiles, the sizes and FPS ranges
1156                 // should be advertise by the camera.
1157                 for (int quality = CamcorderProfile.QUALITY_HIGH_SPEED_480P;
1158                         quality <= CamcorderProfile.QUALITY_HIGH_SPEED_2160P; quality++) {
1159                     if (CamcorderProfile.hasProfile(quality)) {
1160                         CamcorderProfile profile = CamcorderProfile.get(quality);
1161                         Size camcorderProfileSize =
1162                                 new Size(profile.videoFrameWidth, profile.videoFrameHeight);
1163                         assertTrue("CamcorderPrfile size " + camcorderProfileSize +
1164                                 " must be included in the high speed sizes " +
1165                                 Arrays.toString(highSpeedSizes.toArray()),
1166                                 highSpeedSizes.contains(camcorderProfileSize));
1167                         Range<Integer> camcorderFpsRange =
1168                                 new Range<Integer>(profile.videoFrameRate, profile.videoFrameRate);
1169                         List<Range<Integer>> allRanges =
1170                                 Arrays.asList(config.getHighSpeedVideoFpsRangesFor(
1171                                         camcorderProfileSize));
1172                         assertTrue("Camcorder fps range " + camcorderFpsRange +
1173                                 " should be included by high speed fps ranges " +
1174                                 Arrays.toString(allRanges.toArray()),
1175                                 allRanges.contains(camcorderFpsRange));
1176                     }
1177                 }
1178             }
1179             counter++;
1180         }
1181     }
1182 
1183     /**
1184      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
1185      */
findInvalidSize(Size[] goodSizes)1186     private Size findInvalidSize(Size[] goodSizes) {
1187         return findInvalidSize(Arrays.asList(goodSizes));
1188     }
1189 
1190     /**
1191      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
1192      */
findInvalidSize(List<Size> goodSizes)1193     private Size findInvalidSize(List<Size> goodSizes) {
1194         Size invalidSize = new Size(goodSizes.get(0).getWidth() + 1, goodSizes.get(0).getHeight());
1195         while(goodSizes.contains(invalidSize)) {
1196             invalidSize = new Size(invalidSize.getWidth() + 1, invalidSize.getHeight());
1197         }
1198         return invalidSize;
1199     }
1200 
1201     /**
1202      * Check key is present in characteristics if the hardware level is at least {@code hwLevel};
1203      * check that the key is present if the actual capabilities are one of {@code capabilities}.
1204      *
1205      * @return value of the {@code key} from {@code c}
1206      */
expectKeyAvailable(CameraCharacteristics c, CameraCharacteristics.Key<T> key, int hwLevel, int... capabilities)1207     private <T> T expectKeyAvailable(CameraCharacteristics c, CameraCharacteristics.Key<T> key,
1208             int hwLevel, int... capabilities) {
1209 
1210         Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
1211         assertNotNull("android.info.supportedHardwareLevel must never be null", actualHwLevel);
1212 
1213         int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
1214         assertNotNull("android.request.availableCapabilities must never be null",
1215                 actualCapabilities);
1216 
1217         List<Key<?>> allKeys = c.getKeys();
1218 
1219         T value = c.get(key);
1220 
1221         // For LIMITED-level targeted keys, rely on capability check, not level
1222         if ((compareHardwareLevel(actualHwLevel, hwLevel) >= 0) && (hwLevel != LIMITED)) {
1223             mCollector.expectTrue(
1224                     String.format("Key (%s) must be in characteristics for this hardware level " +
1225                             "(required minimal HW level %s, actual HW level %s)",
1226                             key.getName(), toStringHardwareLevel(hwLevel),
1227                             toStringHardwareLevel(actualHwLevel)),
1228                     value != null);
1229             mCollector.expectTrue(
1230                     String.format("Key (%s) must be in characteristics list of keys for this " +
1231                             "hardware level (required minimal HW level %s, actual HW level %s)",
1232                             key.getName(), toStringHardwareLevel(hwLevel),
1233                             toStringHardwareLevel(actualHwLevel)),
1234                     allKeys.contains(key));
1235         } else if (arrayContainsAnyOf(actualCapabilities, capabilities)) {
1236             if (!(hwLevel == LIMITED && compareHardwareLevel(actualHwLevel, hwLevel) < 0)) {
1237                 // Don't enforce LIMITED-starting keys on LEGACY level, even if cap is defined
1238                 mCollector.expectTrue(
1239                     String.format("Key (%s) must be in characteristics for these capabilities " +
1240                             "(required capabilities %s, actual capabilities %s)",
1241                             key.getName(), Arrays.toString(capabilities),
1242                             Arrays.toString(actualCapabilities)),
1243                     value != null);
1244                 mCollector.expectTrue(
1245                     String.format("Key (%s) must be in characteristics list of keys for " +
1246                             "these capabilities (required capabilities %s, actual capabilities %s)",
1247                             key.getName(), Arrays.toString(capabilities),
1248                             Arrays.toString(actualCapabilities)),
1249                     allKeys.contains(key));
1250             }
1251         } else {
1252             if (actualHwLevel == LEGACY && hwLevel != OPT) {
1253                 if (value != null || allKeys.contains(key)) {
1254                     Log.w(TAG, String.format(
1255                             "Key (%s) is not required for LEGACY devices but still appears",
1256                             key.getName()));
1257                 }
1258             }
1259             // OK: Key may or may not be present.
1260         }
1261         return value;
1262     }
1263 
arrayContains(int[] arr, int needle)1264     private static boolean arrayContains(int[] arr, int needle) {
1265         if (arr == null) {
1266             return false;
1267         }
1268 
1269         for (int elem : arr) {
1270             if (elem == needle) {
1271                 return true;
1272             }
1273         }
1274 
1275         return false;
1276     }
1277 
arrayContains(T[] arr, T needle)1278     private static <T> boolean arrayContains(T[] arr, T needle) {
1279         if (arr == null) {
1280             return false;
1281         }
1282 
1283         for (T elem : arr) {
1284             if (elem.equals(needle)) {
1285                 return true;
1286             }
1287         }
1288 
1289         return false;
1290     }
1291 
arrayContainsAnyOf(int[] arr, int[] needles)1292     private static boolean arrayContainsAnyOf(int[] arr, int[] needles) {
1293         for (int needle : needles) {
1294             if (arrayContains(arr, needle)) {
1295                 return true;
1296             }
1297         }
1298         return false;
1299     }
1300 
1301     /**
1302      * The key name has a prefix of either "android." or "com."; other prefixes are not valid.
1303      */
assertKeyPrefixValid(String keyName)1304     private static void assertKeyPrefixValid(String keyName) {
1305         assertStartsWithAnyOf(
1306                 "All metadata keys must start with 'android.' (built-in keys) " +
1307                 "or 'com.' (vendor-extended keys)", new String[] {
1308                         PREFIX_ANDROID + ".",
1309                         PREFIX_VENDOR + ".",
1310                 }, keyName);
1311     }
1312 
assertTrueForKey(String msg, CameraCharacteristics.Key<?> key, boolean actual)1313     private static void assertTrueForKey(String msg, CameraCharacteristics.Key<?> key,
1314             boolean actual) {
1315         assertTrue(msg + " (key = '" + key.getName() + "')", actual);
1316     }
1317 
assertOneOf(String msg, T[] expected, T actual)1318     private static <T> void assertOneOf(String msg, T[] expected, T actual) {
1319         for (int i = 0; i < expected.length; ++i) {
1320             if (Objects.equals(expected[i], actual)) {
1321                 return;
1322             }
1323         }
1324 
1325         fail(String.format("%s: (expected one of %s, actual %s)",
1326                 msg, Arrays.toString(expected), actual));
1327     }
1328 
assertStartsWithAnyOf(String msg, String[] expected, String actual)1329     private static <T> void assertStartsWithAnyOf(String msg, String[] expected, String actual) {
1330         for (int i = 0; i < expected.length; ++i) {
1331             if (actual.startsWith(expected[i])) {
1332                 return;
1333             }
1334         }
1335 
1336         fail(String.format("%s: (expected to start with any of %s, but value was %s)",
1337                 msg, Arrays.toString(expected), actual));
1338     }
1339 
1340     /** Return a positive int if left > right, 0 if left==right, negative int if left < right */
compareHardwareLevel(int left, int right)1341     private static int compareHardwareLevel(int left, int right) {
1342         return remapHardwareLevel(left) - remapHardwareLevel(right);
1343     }
1344 
1345     /** Remap HW levels worst<->best, 0 = worst, 2 = best */
remapHardwareLevel(int level)1346     private static int remapHardwareLevel(int level) {
1347         switch (level) {
1348             case OPT:
1349                 return Integer.MAX_VALUE;
1350             case LEGACY:
1351                 return 0; // lowest
1352             case LIMITED:
1353                 return 1; // second lowest
1354             case FULL:
1355                 return 2; // best
1356         }
1357 
1358         fail("Unknown HW level: " + level);
1359         return -1;
1360     }
1361 
toStringHardwareLevel(int level)1362     private static String toStringHardwareLevel(int level) {
1363         switch (level) {
1364             case LEGACY:
1365                 return "LEGACY";
1366             case LIMITED:
1367                 return "LIMITED";
1368             case FULL:
1369                 return "FULL";
1370         }
1371 
1372         // unknown
1373         Log.w(TAG, "Unknown hardware level " + level);
1374         return Integer.toString(level);
1375     }
1376 }
1377