1 /*
2  * Copyright (C) 2013 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 com.android.mediaframeworktest.unit;
18 
19 import static android.hardware.camera2.impl.CameraMetadataNative.*;
20 
21 import static com.android.mediaframeworktest.unit.ByteArrayHelpers.*;
22 
23 import android.graphics.ImageFormat;
24 import android.graphics.Point;
25 import android.graphics.PointF;
26 import android.graphics.Rect;
27 import android.graphics.SurfaceTexture;
28 import android.hardware.camera2.CameraCharacteristics;
29 import android.hardware.camera2.CameraMetadata;
30 import android.hardware.camera2.CaptureRequest;
31 import android.hardware.camera2.CaptureResult;
32 import android.hardware.camera2.impl.CameraMetadataNative;
33 import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
34 import android.hardware.camera2.params.ColorSpaceTransform;
35 import android.hardware.camera2.params.Face;
36 import android.hardware.camera2.params.HighSpeedVideoConfiguration;
37 import android.hardware.camera2.params.MeteringRectangle;
38 import android.hardware.camera2.params.ReprocessFormatsMap;
39 import android.hardware.camera2.params.RggbChannelVector;
40 import android.hardware.camera2.params.StreamConfiguration;
41 import android.hardware.camera2.params.StreamConfigurationDuration;
42 import android.hardware.camera2.params.StreamConfigurationMap;
43 import android.hardware.camera2.params.TonemapCurve;
44 import android.hardware.camera2.utils.TypeReference;
45 import android.util.Log;
46 import android.util.Pair;
47 import android.util.Range;
48 import android.util.Rational;
49 import android.util.Size;
50 import android.util.SizeF;
51 
52 import androidx.test.filters.SmallTest;
53 
54 import java.lang.reflect.Array;
55 import java.nio.ByteBuffer;
56 import java.nio.ByteOrder;
57 import java.util.List;
58 
59 /**
60  * <pre>
61  * adb shell am instrument \
62  *      -e class 'com.android.mediaframeworktest.unit.CameraMetadataTest' \
63  *      -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
64  * </pre>
65  */
66 public class CameraMetadataTest extends junit.framework.TestCase {
67 
68     private static final boolean VERBOSE = false;
69     private static final String TAG = "CameraMetadataTest";
70 
71 
72     CameraMetadataNative mMetadata;
73 
74     // Sections
75     static final int ANDROID_COLOR_CORRECTION = 0;
76     static final int ANDROID_CONTROL = 1;
77 
78     // Section starts
79     static final int ANDROID_COLOR_CORRECTION_START = ANDROID_COLOR_CORRECTION << 16;
80     static final int ANDROID_CONTROL_START = ANDROID_CONTROL << 16;
81 
82     // Tags
83     static final int ANDROID_COLOR_CORRECTION_MODE = ANDROID_COLOR_CORRECTION_START;
84     static final int ANDROID_COLOR_CORRECTION_TRANSFORM = ANDROID_COLOR_CORRECTION_START + 1;
85     static final int ANDROID_COLOR_CORRECTION_GAINS = ANDROID_COLOR_CORRECTION_START + 2;
86 
87     static final int ANDROID_CONTROL_AE_ANTIBANDING_MODE = ANDROID_CONTROL_START;
88     static final int ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION = ANDROID_CONTROL_START + 1;
89 
90     // From graphics.h
91     private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
92 
93     @Override
setUp()94     public void setUp() {
95         mMetadata = new CameraMetadataNative();
96     }
97 
98     @Override
tearDown()99     public void tearDown() throws Exception {
100         mMetadata = null;
101     }
102 
103     @SmallTest
testNew()104     public void testNew() {
105         assertEquals(0, mMetadata.getEntryCount());
106         assertTrue(mMetadata.isEmpty());
107     }
108 
109     @SmallTest
testGetTagFromKey()110     public void testGetTagFromKey() {
111 
112         // Test success
113 
114         assertEquals(ANDROID_COLOR_CORRECTION_MODE,
115                 CameraMetadataNative.getTag("android.colorCorrection.mode"));
116         assertEquals(ANDROID_COLOR_CORRECTION_TRANSFORM,
117                 CameraMetadataNative.getTag("android.colorCorrection.transform"));
118         assertEquals(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
119                 CameraMetadataNative.getTag("android.control.aeAntibandingMode"));
120         assertEquals(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
121                 CameraMetadataNative.getTag("android.control.aeExposureCompensation"));
122 
123         // Test failures
124 
125         try {
126             CameraMetadataNative.getTag(null);
127             fail("A null key should throw NPE");
128         } catch(NullPointerException e) {
129         }
130 
131         try {
132             CameraMetadataNative.getTag("android.control");
133             fail("A section name only should not be a valid key");
134         } catch(IllegalArgumentException e) {
135         }
136 
137         try {
138             CameraMetadataNative.getTag("android.control.thisTagNameIsFakeAndDoesNotExist");
139             fail("A valid section with an invalid tag name should not be a valid key");
140         } catch(IllegalArgumentException e) {
141         }
142 
143         try {
144             CameraMetadataNative.getTag("android");
145             fail("A namespace name only should not be a valid key");
146         } catch(IllegalArgumentException e) {
147         }
148 
149         try {
150             CameraMetadataNative.getTag("this.key.is.definitely.invalid");
151             fail("A completely fake key name should not be valid");
152         } catch(IllegalArgumentException e) {
153         }
154     }
155 
156     @SmallTest
testGetTypeFromTag()157     public void testGetTypeFromTag() {
158         assertEquals(TYPE_BYTE,
159                 CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE, Long.MAX_VALUE));
160         assertEquals(TYPE_RATIONAL,
161                 CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM, Long.MAX_VALUE));
162         assertEquals(TYPE_FLOAT,
163                 CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS, Long.MAX_VALUE));
164         assertEquals(TYPE_BYTE,
165                 CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE, Long.MAX_VALUE));
166         assertEquals(TYPE_INT32,
167                 CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, Long.MAX_VALUE));
168 
169         try {
170             CameraMetadataNative.getNativeType(0xDEADF00D, Long.MAX_VALUE);
171             fail("No type should exist for invalid tag 0xDEADF00D");
172         } catch(IllegalArgumentException e) {
173         }
174     }
175 
176     @SmallTest
testReadWriteValues()177     public void testReadWriteValues() {
178         final byte ANDROID_COLOR_CORRECTION_MODE_HIGH_QUALITY = 2;
179         byte[] valueResult;
180 
181         assertEquals(0, mMetadata.getEntryCount());
182         assertEquals(true, mMetadata.isEmpty());
183 
184         //
185         // android.colorCorrection.mode (single enum byte)
186         //
187 
188         assertEquals(null, mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE));
189 
190         // Write/read null values
191         mMetadata.writeValues(ANDROID_COLOR_CORRECTION_MODE, null);
192         assertEquals(null, mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE));
193 
194         // Write 0 values
195         mMetadata.writeValues(ANDROID_COLOR_CORRECTION_MODE, new byte[] {});
196 
197         // Read 0 values
198         valueResult = mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE);
199         assertNotNull(valueResult);
200         assertEquals(0, valueResult.length);
201 
202         assertEquals(1, mMetadata.getEntryCount());
203         assertEquals(false, mMetadata.isEmpty());
204 
205         // Write 1 value
206         mMetadata.writeValues(ANDROID_COLOR_CORRECTION_MODE, new byte[] {
207             ANDROID_COLOR_CORRECTION_MODE_HIGH_QUALITY
208         });
209 
210         // Read 1 value
211         valueResult = mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE);
212         assertNotNull(valueResult);
213         assertEquals(1, valueResult.length);
214         assertEquals(ANDROID_COLOR_CORRECTION_MODE_HIGH_QUALITY, valueResult[0]);
215 
216         assertEquals(1, mMetadata.getEntryCount());
217         assertEquals(false, mMetadata.isEmpty());
218 
219         //
220         // android.colorCorrection.colorCorrectionGains (float x 4 array)
221         //
222 
223         final float[] colorCorrectionGains = new float[] { 1.0f, 2.0f, 3.0f, 4.0f};
224         byte[] colorCorrectionGainsAsByteArray = new byte[colorCorrectionGains.length * 4];
225         ByteBuffer colorCorrectionGainsByteBuffer =
226                 ByteBuffer.wrap(colorCorrectionGainsAsByteArray).order(ByteOrder.nativeOrder());
227         for (float f : colorCorrectionGains)
228             colorCorrectionGainsByteBuffer.putFloat(f);
229 
230         // Read
231         assertNull(mMetadata.readValues(ANDROID_COLOR_CORRECTION_GAINS));
232         mMetadata.writeValues(ANDROID_COLOR_CORRECTION_GAINS, colorCorrectionGainsAsByteArray);
233 
234         // Write
235         assertArrayEquals(colorCorrectionGainsAsByteArray,
236                 mMetadata.readValues(ANDROID_COLOR_CORRECTION_GAINS));
237 
238         assertEquals(2, mMetadata.getEntryCount());
239         assertEquals(false, mMetadata.isEmpty());
240 
241         // Erase
242         mMetadata.writeValues(ANDROID_COLOR_CORRECTION_GAINS, null);
243         assertNull(mMetadata.readValues(ANDROID_COLOR_CORRECTION_GAINS));
244         assertEquals(1, mMetadata.getEntryCount());
245     }
246 
247     /**
248      * Format an array into a string with the {@code badIndex} highlighted with {@code **}.
249      *
250      * <p>Numbers are printed as hexadecimal values.</p>
251      *
252      * <p>Example: {@code "[hello, **world**]"} for a {@code string[]},
253      * or a {@code "[**0xFF**, 0xFF]"} for a {@code int[]}.</p>
254      */
formatArray(T array, int badIndex)255     private static <T> String formatArray(T array, int badIndex) {
256         StringBuilder builder = new StringBuilder();
257 
258         builder.append("[");
259 
260         int len = Array.getLength(array);
261         for (int i = 0; i < len; ++i) {
262 
263             Object elem = Array.get(array, i);
264 
265             if (i == badIndex) {
266                 builder.append("**");
267             }
268 
269             if (elem instanceof Byte) {
270                 builder.append(String.format("%x", (Byte) elem));
271             } else if (elem instanceof Short) {
272                 builder.append(String.format("%x", (Short) elem));
273             } else if (elem instanceof Integer) {
274                 builder.append(String.format("%x", (Integer) elem));
275             } else if (elem instanceof Long) {
276                 builder.append(String.format("%x", (Long) elem));
277             } else {
278                 builder.append(elem);
279             }
280 
281             if (i == badIndex) {
282                 builder.append("**");
283             }
284 
285             if (i != len - 1) {
286                 builder.append(", ");
287             }
288         }
289 
290         builder.append("]");
291 
292         return builder.toString();
293     }
294 
assertArrayEquals(T expected, T actual)295     private static <T> void assertArrayEquals(T expected, T actual) {
296         if (!expected.getClass().isArray() || !actual.getClass().isArray()) {
297             throw new IllegalArgumentException("expected, actual must both be arrays");
298         }
299 
300         assertEquals("Array lengths must be equal",
301                 Array.getLength(expected), Array.getLength(actual));
302 
303         int len = Array.getLength(expected);
304         for (int i = 0; i < len; ++i) {
305 
306             Object expectedElement = Array.get(expected, i);
307             Object actualElement = Array.get(actual, i);
308 
309             if (!expectedElement.equals(actualElement)) {
310                 fail(String.format(
311                         "element %d in array was not equal (expected %s, actual %s). "
312                                 + "Arrays were: (expected %s, actual %s).",
313                                 i, expectedElement, actualElement,
314                                 formatArray(expected, i),
315                                 formatArray(actual, i)));
316             }
317         }
318     }
319 
assertArrayContains(T needle, T2 array)320     private static <T, T2> void assertArrayContains(T needle, T2 array) {
321         if (!array.getClass().isArray()) {
322             throw new IllegalArgumentException("actual must be array");
323         }
324 
325         int len = Array.getLength(array);
326         for (int i = 0; i < len; ++i) {
327 
328             Object actualElement = Array.get(array, i);
329 
330             if (needle.equals(actualElement)) {
331                 return;
332             }
333         }
334 
335         fail(String.format(
336                 "could not find element in array (needle %s). "
337                         + "Array was: %s.",
338                         needle,
339                         formatArray(array, len)));
340     }
341 
checkKeyGetAndSet(String keyStr, TypeReference<T> typeToken, T expected, boolean reuse)342     private <T> void checkKeyGetAndSet(String keyStr, TypeReference<T> typeToken, T expected,
343             boolean reuse) {
344         Key<T> key = new Key<T>(keyStr, typeToken);
345         assertNull(mMetadata.get(key));
346         mMetadata.set(key, null);
347         assertNull(mMetadata.get(key));
348         mMetadata.set(key, expected);
349 
350         T actual = mMetadata.get(key);
351 
352         if (typeToken.getRawType().isArray()) {
353             assertArrayEquals(expected, actual);
354         } else {
355             assertEquals(expected, actual);
356         }
357 
358         if (reuse) {
359             // reset the key incase we want to use it again
360             mMetadata.set(key, null);
361         }
362     }
363 
checkKeyGetAndSet(String keyStr, TypeReference<T> typeToken, T expected)364     private <T> void checkKeyGetAndSet(String keyStr, TypeReference<T> typeToken, T expected) {
365         checkKeyGetAndSet(keyStr, typeToken, expected, /*reuse*/false);
366     }
367 
checkKeyGetAndSet(String keyStr, Class<T> type, T expected)368     private <T> void checkKeyGetAndSet(String keyStr, Class<T> type, T expected) {
369         checkKeyGetAndSet(keyStr, TypeReference.createSpecializedTypeReference(type), expected);
370     }
371 
372     /**
373      * Ensure that the data survives a marshal/unmarshal round-trip;
374      * it must also be equal to the {@code expectedNative} byte array.
375      *
376      * <p>As a side-effect, the metadata value corresponding to the key is now set to
377      * {@code expected}.</p>
378      *
379      * @return key created with {@code keyName} and {@code T}
380      */
checkKeyMarshal(String keyName, TypeReference<T> typeReference, T expected, byte[] expectedNative)381     private <T> Key<T> checkKeyMarshal(String keyName, TypeReference<T> typeReference,
382             T expected, byte[] expectedNative) {
383         Key<T> key = new Key<T>(keyName, typeReference);
384 
385         mMetadata.set(key, null);
386         assertNull(mMetadata.get(key));
387 
388         // Write managed value -> make sure native bytes are what we expect
389         mMetadata.set(key, expected);
390 
391         byte[] actualValues = mMetadata.readValues(key.getTag());
392         assertArrayEquals(expectedNative, actualValues);
393 
394         // Write managed value -> make sure read-out managed value is what we expect
395         T actual = mMetadata.get(key);
396 
397         if (typeReference.getRawType().isArray()) {
398             assertArrayEquals(expected, actual);
399         } else {
400             assertEquals(expected, actual);
401         }
402 
403         // Write native bytes -> make sure read-out managed value is what we expect
404         mMetadata.writeValues(key.getTag(), expectedNative);
405         actual = mMetadata.get(key);
406 
407         if (typeReference.getRawType().isArray()) {
408             assertArrayEquals(expected, actual);
409         } else {
410             assertEquals(expected, actual);
411         }
412 
413         return key;
414     }
415 
416     /**
417      * Ensure that the data survives a marshal/unmarshal round-trip;
418      * it must also be equal to the {@code expectedNative} byte array.
419      *
420      * <p>As a side-effect,
421      * the metadata value corresponding to the key is now set to {@code expected}.</p>
422      *
423      * @return key created with {@code keyName} and {@code T}
424      */
checkKeyMarshal(String keyName, T expected, byte[] expectedNative)425     private <T> Key<T> checkKeyMarshal(String keyName, T expected, byte[] expectedNative) {
426         @SuppressWarnings("unchecked")
427         Class<T> expectedClass = (Class<T>) expected.getClass();
428         return checkKeyMarshal(keyName,
429                 TypeReference.createSpecializedTypeReference(expectedClass),
430                 expected,
431                 expectedNative);
432     }
433 
434     @SmallTest
testReadWritePrimitive()435     public void testReadWritePrimitive() {
436         // int32 (single)
437         checkKeyGetAndSet("android.control.aeExposureCompensation", Integer.TYPE, 0xC0FFEE);
438 
439         // byte (single)
440         checkKeyGetAndSet("android.flash.maxEnergy", Byte.TYPE, (byte)6);
441 
442         // int64 (single)
443         checkKeyGetAndSet("android.flash.firingTime", Long.TYPE, 0xABCD12345678FFFFL);
444 
445         // float (single)
446         checkKeyGetAndSet("android.lens.aperture", Float.TYPE, Float.MAX_VALUE);
447 
448         // double (single) -- technically double x 3, but we fake it
449         checkKeyGetAndSet("android.jpeg.gpsCoordinates", Double.TYPE, Double.MAX_VALUE);
450 
451         // rational (single)
452         checkKeyGetAndSet("android.sensor.baseGainFactor", Rational.class, new Rational(1, 2));
453 
454         /**
455          * Weirder cases, that don't map 1:1 with the native types
456          */
457 
458         // bool (single) -- with TYPE_BYTE
459         checkKeyGetAndSet("android.control.aeLock", Boolean.TYPE, true);
460 
461         // integer (single) -- with TYPE_BYTE
462         checkKeyGetAndSet("android.control.aePrecaptureTrigger", Integer.TYPE, 6);
463     }
464 
465     @SmallTest
testReadWritePrimitiveArray()466     public void testReadWritePrimitiveArray() {
467         // int32 (n)
468         checkKeyGetAndSet("android.sensor.info.sensitivityRange", int[].class,
469                 new int[] {
470                         0xC0FFEE, 0xDEADF00D
471                 });
472 
473         // byte (n)
474         checkKeyGetAndSet("android.statistics.faceScores", byte[].class, new byte[] {
475                 1, 2, 3, 4
476         });
477 
478         // int64 (n)
479         checkKeyGetAndSet("android.scaler.availableProcessedMinDurations", long[].class,
480                 new long[] {
481                         0xABCD12345678FFFFL, 0x1234ABCD5678FFFFL, 0xFFFF12345678ABCDL
482                 });
483 
484         // float (n)
485         checkKeyGetAndSet("android.lens.info.availableApertures", float[].class,
486                 new float[] {
487                         Float.MAX_VALUE, Float.MIN_NORMAL, Float.MIN_VALUE
488                 });
489 
490         // double (n) -- in particular double x 3
491         checkKeyGetAndSet("android.jpeg.gpsCoordinates", double[].class,
492                 new double[] {
493                         Double.MAX_VALUE, Double.MIN_NORMAL, Double.MIN_VALUE
494                 });
495 
496         // rational (n) -- in particular rational x 9
497         checkKeyGetAndSet("android.sensor.calibrationTransform1", Rational[].class,
498                 new Rational[] {
499                         new Rational(1, 2), new Rational(3, 4), new Rational(5, 6),
500                         new Rational(7, 8), new Rational(9, 10), new Rational(10, 11),
501                         new Rational(12, 13), new Rational(14, 15), new Rational(15, 16)
502                 });
503 
504         /**
505          * Weirder cases, that don't map 1:1 with the native types
506          */
507 
508         // bool (n) -- with TYPE_BYTE
509         checkKeyGetAndSet("android.control.aeLock", boolean[].class, new boolean[] {
510                 true, false, true
511         });
512 
513         // integer (n) -- with TYPE_BYTE
514         checkKeyGetAndSet("android.control.aeAvailableModes", int[].class, new int[] {
515             1, 2, 3, 4
516         });
517     }
518 
519     private enum ColorCorrectionMode {
520         TRANSFORM_MATRIX,
521         FAST,
522         HIGH_QUALITY
523     }
524 
525     private enum AeAntibandingMode {
526         OFF,
527         _50HZ,
528         _60HZ,
529         AUTO
530     }
531 
532     private enum AvailableFormat {
533         RAW_SENSOR,
534         YV12,
535         YCrCb_420_SP,
536         IMPLEMENTATION_DEFINED,
537         YCbCr_420_888,
538         BLOB
539     }
540 
541     @SmallTest
testReadWriteEnum()542     public void testReadWriteEnum() {
543         // byte (single)
544         checkKeyGetAndSet("android.colorCorrection.mode", ColorCorrectionMode.class,
545                 ColorCorrectionMode.HIGH_QUALITY);
546 
547         // byte (single)
548         checkKeyGetAndSet("android.control.aeAntibandingMode", AeAntibandingMode.class,
549                 AeAntibandingMode.AUTO);
550 
551         // byte (n)
552         checkKeyGetAndSet("android.control.aeAvailableAntibandingModes",
553                 AeAntibandingMode[].class, new AeAntibandingMode[] {
554                         AeAntibandingMode.OFF, AeAntibandingMode._50HZ, AeAntibandingMode._60HZ,
555                         AeAntibandingMode.AUTO
556                 });
557 
558         /**
559          * Stranger cases that don't use byte enums
560          */
561         // int (n)
562         checkKeyGetAndSet("android.scaler.availableFormats", AvailableFormat[].class,
563                 new AvailableFormat[] {
564                         AvailableFormat.RAW_SENSOR,
565                         AvailableFormat.YV12,
566                         AvailableFormat.IMPLEMENTATION_DEFINED,
567                         AvailableFormat.YCbCr_420_888,
568                         AvailableFormat.BLOB
569                 });
570 
571     }
572 
573     @SmallTest
testReadWriteEnumWithCustomValues()574     public void testReadWriteEnumWithCustomValues() {
575         MarshalQueryableEnum.registerEnumValues(AeAntibandingMode.class, new int[] {
576             0,
577             10,
578             20,
579             30
580         });
581 
582         // byte (single)
583         checkKeyGetAndSet("android.control.aeAntibandingMode", AeAntibandingMode.class,
584                 AeAntibandingMode.AUTO);
585 
586         // byte (n)
587         checkKeyGetAndSet("android.control.aeAvailableAntibandingModes",
588                 AeAntibandingMode[].class, new AeAntibandingMode[] {
589                         AeAntibandingMode.OFF, AeAntibandingMode._50HZ, AeAntibandingMode._60HZ,
590                         AeAntibandingMode.AUTO
591                 });
592 
593         byte[] aeAntibandingModeValues = mMetadata.readValues(CameraMetadataNative
594                 .getTag("android.control.aeAvailableAntibandingModes"));
595         byte[] expectedValues = new byte[] { 0, 10, 20, 30 };
596         assertArrayEquals(expectedValues, aeAntibandingModeValues);
597 
598 
599         /**
600          * Stranger cases that don't use byte enums
601          */
602         // int (n)
603         MarshalQueryableEnum.registerEnumValues(AvailableFormat.class, new int[] {
604             0x20,
605             0x32315659,
606             0x11,
607             0x22,
608             0x23,
609             0x21,
610         });
611 
612         checkKeyGetAndSet("android.scaler.availableFormats", AvailableFormat[].class,
613                 new AvailableFormat[] {
614                         AvailableFormat.RAW_SENSOR,
615                         AvailableFormat.YV12,
616                         AvailableFormat.IMPLEMENTATION_DEFINED,
617                         AvailableFormat.YCbCr_420_888,
618                         AvailableFormat.BLOB
619                 });
620 
621         Key<AvailableFormat[]> availableFormatsKey =
622                 new Key<AvailableFormat[]>("android.scaler.availableFormats",
623                         AvailableFormat[].class);
624         byte[] availableFormatValues = mMetadata.readValues(CameraMetadataNative
625                 .getTag(availableFormatsKey.getName()));
626 
627         int[] expectedIntValues = new int[] {
628                 0x20,
629                 0x32315659,
630                 0x22,
631                 0x23,
632                 0x21
633         };
634 
635         ByteBuffer bf = ByteBuffer.wrap(availableFormatValues).order(ByteOrder.nativeOrder());
636 
637         assertEquals(expectedIntValues.length * 4, availableFormatValues.length);
638         for (int i = 0; i < expectedIntValues.length; ++i) {
639             assertEquals(expectedIntValues[i], bf.getInt());
640         }
641     }
642 
643     @SmallTest
testReadWriteSize()644     public void testReadWriteSize() {
645         // int32 x n
646         checkKeyGetAndSet("android.jpeg.thumbnailSize", Size.class, new Size(123, 456));
647 
648         // int32 x 2 x n
649         checkKeyGetAndSet("android.scaler.availableJpegSizes", Size[].class, new Size[] {
650             new Size(123, 456),
651             new Size(0xDEAD, 0xF00D),
652             new Size(0xF00, 0xB00)
653         });
654     }
655 
656     @SmallTest
testReadWriteRggbChannelVector()657     public void testReadWriteRggbChannelVector() {
658         // int32 x n
659         checkKeyMarshal("android.colorCorrection.gains",
660                 new RggbChannelVector(1.0f, 2.1f, 3.2f, 4.5f),
661                 toByteArray(1.0f, 2.1f, 3.2f, 4.5f));
662 
663         // int32 x 2 x n [pretend; actual is not array]
664         checkKeyMarshal("android.colorCorrection.gains",
665                 new RggbChannelVector[] {
666                     new RggbChannelVector(1.0f, 2.0f, 3.0f, 4.0f),
667                     new RggbChannelVector(9.0f, 8.0f, 7.0f, 6.0f),
668                     new RggbChannelVector(1.3f, 5.5f, 2.4f, 6.7f),
669                 }, toByteArray(
670                         1.0f, 2.0f, 3.0f, 4.0f,
671                         9.0f, 8.0f, 7.0f, 6.0f,
672                         1.3f, 5.5f, 2.4f, 6.7f
673                 ));
674     }
675 
676     @SmallTest
testReadWriteSizeF()677     public void testReadWriteSizeF() {
678         // int32 x n
679         checkKeyMarshal("android.sensor.info.physicalSize",
680                 new SizeF(123f, 456f),
681                 toByteArray(123f, 456f));
682 
683         // int32 x 2 x n
684         checkKeyMarshal("android.sensor.info.physicalSize",
685                 new SizeF[] {
686                     new SizeF(123f, 456f),
687                     new SizeF(1.234f, 4.567f),
688                     new SizeF(999.0f, 555.0f)
689                 },
690                 toByteArray(
691                         123f, 456f,
692                         1.234f, 4.567f,
693                         999.0f, 555.0f)
694         );
695     }
696 
697     @SmallTest
testReadWriteRectangle()698     public void testReadWriteRectangle() {
699         // int32 x n
700         checkKeyMarshal("android.scaler.cropRegion",
701                 // x1, y1, x2, y2
702                 new Rect(10, 11, 1280, 1024),
703                 // x, y, width, height
704                 toByteArray(10, 11, 1280 - 10, 1024 - 11));
705 
706         // int32 x 2 x n  [actually not array, but we pretend it is]
707         checkKeyMarshal("android.scaler.cropRegion", new Rect[] {
708             new Rect(110, 111, 11280, 11024),
709             new Rect(210, 111, 21280, 21024),
710             new Rect(310, 111, 31280, 31024)
711         }, toByteArray(
712                 110, 111, 11280 - 110, 11024 - 111,
713                 210, 111, 21280 - 210, 21024 - 111,
714                 310, 111, 31280 - 310, 31024 - 111
715         ));
716     }
717 
718     @SmallTest
testReadWriteMeteringRectangle()719     public void testReadWriteMeteringRectangle() {
720         // int32 x 5 x area_count [but we pretend it's a single element]
721         checkKeyMarshal("android.control.aeRegions",
722                 new MeteringRectangle(/*x*/1, /*y*/2, /*width*/100, /*height*/200, /*weight*/5),
723                 /* xmin, ymin, xmax, ymax, weight */
724                 toByteArray(1, 2, 1 + 100, 2 + 200, 5));
725 
726         // int32 x 5 x area_count
727         checkKeyMarshal("android.control.afRegions",
728                 new MeteringRectangle[] {
729                     new MeteringRectangle(/*x*/5, /*y*/6, /*width*/123, /*height*/456, /*weight*/7),
730                     new MeteringRectangle(/*x*/7, /*y*/8, /*width*/456, /*height*/999, /*weight*/6),
731                     new MeteringRectangle(/*x*/1, /*y*/2, /*width*/100, /*height*/200, /*weight*/5)
732                 },
733                 toByteArray(
734                         5, 6, 5 + 123, 6 + 456, 7,
735                         7, 8, 7 + 456, 8 + 999, 6,
736                         1, 2, 1 + 100, 2 + 200, 5
737         ));
738     }
739 
740     @SmallTest
testReadWriteHighSpeedVideoConfiguration()741     public void testReadWriteHighSpeedVideoConfiguration() {
742         // int32 x 5 x 1
743         checkKeyMarshal("android.control.availableHighSpeedVideoConfigurations",
744                 new HighSpeedVideoConfiguration(
745                         /*width*/1000, /*height*/255, /*fpsMin*/30, /*fpsMax*/200,
746                         /*batchSizeMax*/8),
747                 /* width, height, fpsMin, fpsMax */
748                 toByteArray(1000, 255, 30, 200, 8));
749 
750         // int32 x 5 x 3
751         checkKeyMarshal("android.control.availableHighSpeedVideoConfigurations",
752                 new HighSpeedVideoConfiguration[] {
753                     new HighSpeedVideoConfiguration(
754                             /*width*/1280, /*height*/720, /*fpsMin*/60, /*fpsMax*/120,
755                             /*batchSizeMax*/8),
756                     new HighSpeedVideoConfiguration(
757                             /*width*/123, /*height*/456, /*fpsMin*/1, /*fpsMax*/200,
758                             /*batchSizeMax*/4),
759                     new HighSpeedVideoConfiguration(
760                             /*width*/4096, /*height*/2592, /*fpsMin*/30, /*fpsMax*/60,
761                             /*batchSizeMax*/2)
762                 },
763                 toByteArray(
764                         1280, 720, 60, 120, 8,
765                         123, 456, 1, 200, 4,
766                         4096, 2592, 30, 60, 2
767         ));
768     }
769 
770     @SmallTest
testReadWriteColorSpaceTransform()771     public void testReadWriteColorSpaceTransform() {
772         // rational x 3 x 3
773         checkKeyMarshal("android.colorCorrection.transform",
774                 new ColorSpaceTransform(new Rational[] {
775                         new Rational(1, 2), new Rational(3, 4), new Rational(5, 6),
776                         new Rational(7, 8), new Rational(8, 9), new Rational(10, 11),
777                         new Rational(1, 5), new Rational(2, 8), new Rational(3, 9),
778                 }),
779                 toByteArray(
780                         1, 2, 3, 4, 5, 6,
781                         7, 8, 8, 9, 10, 11,
782                         1, 5, 1, 4, 1, 3));
783     }
784 
785     @SmallTest
testReadWritePoint()786     public void testReadWritePoint() {
787         // int32 x 2 [actually 'x n' but pretend it's a single value for now]
788         checkKeyMarshal("android.statistics.hotPixelMap",
789                 new Point(1, 2),
790                 toByteArray(1, 2));
791 
792         // int32 x 2 x samples
793         checkKeyMarshal("android.statistics.hotPixelMap",
794                 new Point[] {
795                     new Point(1, 2),
796                     new Point(3, 4),
797                     new Point(5, 6),
798                     new Point(7, 8),
799                 },
800                 toByteArray(
801                         1, 2,
802                         3, 4,
803                         5, 6,
804                         7, 8)
805         );
806     }
807 
808     @SmallTest
testReadWritePointF()809     public void testReadWritePointF() {
810         // float x 2 [actually 'x samples' but pretend it's a single value for now]
811         checkKeyMarshal(
812                 "android.sensor.profileToneCurve",
813                 new PointF(1.0f, 2.0f),
814                 toByteArray(1.0f, 2.0f));
815 
816         // float x 2 x samples
817         checkKeyMarshal("android.sensor.profileToneCurve",
818                 new PointF[] {
819                     new PointF(1.0f, 2.0f),
820                     new PointF(3.0f, 4.0f),
821                     new PointF(5.0f, 6.0f),
822                     new PointF(7.0f, 8.0f),
823                 },
824                 toByteArray(
825                         1.0f, 2.0f,
826                         3.0f, 4.0f,
827                         5.0f, 6.0f,
828                         7.0f, 8.0f));
829     }
830 
831     @SmallTest
testReadWritePair()832     public void testReadWritePair() {
833         // float x 2
834         checkKeyMarshal("android.lens.focusRange",
835                 new TypeReference<Pair<Float, Float>>() {{ }},
836                 Pair.create(1.0f / 2.0f, 1.0f / 3.0f),
837                 toByteArray(1.0f / 2.0f, 1.0f / 3.0f));
838 
839         // byte, int (fake from TYPE_BYTE)
840         // This takes advantage of the TYPE_BYTE -> int marshaler designed for enums.
841         checkKeyMarshal("android.flash.mode",
842                 new TypeReference<Pair<Byte, Integer>>() {{ }},
843                 Pair.create((byte)123, 22),
844                 toByteArray((byte)123, (byte)22));
845     }
846 
847     @SmallTest
testReadWriteRange()848     public void testReadWriteRange() {
849         // int32 x 2
850         checkKeyMarshal("android.control.aeTargetFpsRange",
851                 new TypeReference<Range<Integer>>() {{ }},
852                 Range.create(123, 456),
853                 toByteArray(123, 456));
854 
855         // int64 x 2
856         checkKeyMarshal("android.sensor.info.exposureTimeRange",
857                 new TypeReference<Range<Long>>() {{ }},
858                 Range.create(123L, 456L),
859                 toByteArray(123L, 456L));
860     }
861 
862     @SmallTest
testReadWriteStreamConfiguration()863     public void testReadWriteStreamConfiguration() {
864         // int32 x 4 x n
865         checkKeyMarshal("android.scaler.availableStreamConfigurations",
866                 new StreamConfiguration[] {
867                     new StreamConfiguration(ImageFormat.YUV_420_888, 640, 480, /*input*/false),
868                     new StreamConfiguration(ImageFormat.RGB_565, 320, 240, /*input*/true),
869                 },
870                 toByteArray(
871                         ImageFormat.YUV_420_888, 640, 480, /*input*/0,
872                         ImageFormat.RGB_565, 320, 240, /*input*/1)
873         );
874     }
875 
876     @SmallTest
testReadWriteStreamConfigurationDuration()877     public void testReadWriteStreamConfigurationDuration() {
878         // Avoid sign extending ints when converting to a long
879         final long MASK_UNSIGNED_INT = 0x00000000ffffffffL;
880 
881         // int64 x 4 x n
882         checkKeyMarshal("android.scaler.availableMinFrameDurations",
883                 new StreamConfigurationDuration[] {
884                     new StreamConfigurationDuration(
885                             ImageFormat.YUV_420_888, 640, 480, /*duration*/123L),
886                     new StreamConfigurationDuration(
887                             ImageFormat.RGB_565, 320, 240, /*duration*/345L),
888                 },
889                 toByteArray(
890                         ImageFormat.YUV_420_888 & MASK_UNSIGNED_INT, 640L, 480L, /*duration*/123L,
891                         ImageFormat.RGB_565 & MASK_UNSIGNED_INT, 320L, 240L, /*duration*/345L)
892         );
893     }
894 
895 
896     @SmallTest
testReadWriteReprocessFormatsMap()897     public void testReadWriteReprocessFormatsMap() {
898 
899         // final int RAW_OPAQUE = 0x24; // TODO: add RAW_OPAQUE to ImageFormat
900         final int RAW16 = ImageFormat.RAW_SENSOR;
901         final int YUV_420_888 = ImageFormat.YUV_420_888;
902         final int BLOB = 0x21;
903 
904         // TODO: also test HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED as an output
905         int[] contents = new int[] {
906                 YUV_420_888, 3, YUV_420_888, ImageFormat.NV21, BLOB,
907                 RAW16, 2, YUV_420_888, BLOB,
908 
909         };
910 
911         // int32 x n
912         Key<ReprocessFormatsMap> key = new Key<ReprocessFormatsMap>(
913                 "android.scaler.availableInputOutputFormatsMap", ReprocessFormatsMap.class);
914         mMetadata.writeValues(key.getTag(), toByteArray(contents));
915 
916         ReprocessFormatsMap map = mMetadata.get(key);
917 
918         /*
919          * Make sure the inputs/outputs were what we expected.
920          * - Use public image format constants here.
921          */
922 
923         int[] expectedInputs = new int[] {
924                 YUV_420_888, RAW16
925         };
926         assertArrayEquals(expectedInputs, map.getInputs());
927 
928         int[] expectedYuvOutputs = new int[] {
929                 YUV_420_888, ImageFormat.NV21, ImageFormat.JPEG,
930         };
931         assertArrayEquals(expectedYuvOutputs, map.getOutputs(ImageFormat.YUV_420_888));
932 
933         int[] expectedRaw16Outputs = new int[] {
934                 YUV_420_888, ImageFormat.JPEG,
935         };
936         assertArrayEquals(expectedRaw16Outputs, map.getOutputs(ImageFormat.RAW_SENSOR));
937 
938         // Finally, do a round-trip check for consistency
939         checkKeyMarshal(
940                 "android.scaler.availableInputOutputFormatsMap",
941                 new ReprocessFormatsMap(contents),
942                 toByteArray(contents)
943         );
944     }
945 
946     @SmallTest
testReadWriteString()947     public void testReadWriteString() {
948         // (byte) string
949         Key<String> gpsProcessingMethodKey =
950                 new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
951 
952         String helloWorld = new String("HelloWorld");
953         byte[] helloWorldBytes = new byte[] {
954                 'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0' };
955 
956         mMetadata.set(gpsProcessingMethodKey, helloWorld);
957 
958         String actual = mMetadata.get(gpsProcessingMethodKey);
959         assertEquals(helloWorld, actual);
960 
961         byte[] actualBytes = mMetadata.readValues(getTag(gpsProcessingMethodKey.getName()));
962         assertArrayEquals(helloWorldBytes, actualBytes);
963 
964         // Does not yet test as a string[] since we don't support that in native code.
965 
966         // (byte) string
967         Key<String[]> gpsProcessingMethodKeyArray =
968                 new Key<String[]>("android.jpeg.gpsProcessingMethod", String[].class);
969 
970         String[] gpsStrings = new String[] { "HelloWorld", "FooBar", "Shazbot" };
971         byte[] gpsBytes = new byte[] {
972                 'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0',
973                 'F', 'o', 'o', 'B', 'a', 'r', '\0',
974                 'S', 'h', 'a', 'z', 'b', 'o', 't', '\0'};
975 
976         mMetadata.set(gpsProcessingMethodKeyArray, gpsStrings);
977 
978         String[] actualArray = mMetadata.get(gpsProcessingMethodKeyArray);
979         assertArrayEquals(gpsStrings, actualArray);
980 
981         byte[] actualBytes2 = mMetadata.readValues(getTag(gpsProcessingMethodKeyArray.getName()));
982         assertArrayEquals(gpsBytes, actualBytes2);
983     }
984 
985     @SmallTest
testReadWriteOverride()986     public void testReadWriteOverride() {
987         //
988         // android.scaler.availableFormats (int x n array)
989         //
990         int[] availableFormats = new int[] {
991                 0x20,       // RAW_SENSOR
992                 0x32315659, // YV12
993                 0x11,       // YCrCb_420_SP
994                 0x100,      // ImageFormat.JPEG
995                 0x22,       // IMPLEMENTATION_DEFINED
996                 0x23,       // YCbCr_420_888
997         };
998         int[] expectedIntValues = new int[] {
999                 0x20,       // RAW_SENSOR
1000                 0x32315659, // YV12
1001                 0x11,       // YCrCb_420_SP
1002                 0x21,       // BLOB
1003                 0x22,       // IMPLEMENTATION_DEFINED
1004                 0x23,       // YCbCr_420_888
1005         };
1006         int availableFormatTag = CameraMetadataNative.getTag("android.scaler.availableFormats");
1007 
1008         Key<int[]> formatKey = CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey();
1009 
1010         validateArrayMetadataReadWriteOverride(formatKey, availableFormats,
1011                 expectedIntValues, availableFormatTag);
1012 
1013         //
1014         // android.statistics.faces (Face x n array)
1015         //
1016         int[] expectedFaceIds = new int[] {1, 2, 3, 4, 5};
1017         byte[] expectedFaceScores = new byte[] {10, 20, 30, 40, 50};
1018         int numFaces = expectedFaceIds.length;
1019         Rect[] expectedRects = new Rect[numFaces];
1020         for (int i = 0; i < numFaces; i++) {
1021             expectedRects[i] = new Rect(i*4 + 1, i * 4 + 2, i * 4 + 3, i * 4 + 4);
1022         }
1023         int[] expectedFaceLM = new int[] {
1024                 1, 2, 3, 4, 5, 6,
1025                 7, 8, 9, 10, 11, 12,
1026                 13, 14, 15, 16, 17, 18,
1027                 19, 20, 21, 22, 23, 24,
1028                 25, 26, 27, 28, 29, 30,
1029         };
1030         Point[] expectedFaceLMPoints = new Point[numFaces * 3];
1031         for (int i = 0; i < numFaces; i++) {
1032             expectedFaceLMPoints[i*3] = new Point(expectedFaceLM[i*6], expectedFaceLM[i*6+1]);
1033             expectedFaceLMPoints[i*3+1] = new Point(expectedFaceLM[i*6+2], expectedFaceLM[i*6+3]);
1034             expectedFaceLMPoints[i*3+2] = new Point(expectedFaceLM[i*6+4], expectedFaceLM[i*6+5]);
1035         }
1036 
1037         /**
1038          * Read - FACE_DETECT_MODE == FULL
1039          */
1040         mMetadata.set(CaptureResult.STATISTICS_FACE_DETECT_MODE,
1041                 CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL);
1042         mMetadata.set(CaptureResult.STATISTICS_FACE_IDS, expectedFaceIds);
1043         mMetadata.set(CaptureResult.STATISTICS_FACE_SCORES, expectedFaceScores);
1044         mMetadata.set(CaptureResult.STATISTICS_FACE_RECTANGLES, expectedRects);
1045         mMetadata.set(CaptureResult.STATISTICS_FACE_LANDMARKS, expectedFaceLM);
1046         Face[] resultFaces = mMetadata.get(CaptureResult.STATISTICS_FACES);
1047         assertEquals(numFaces, resultFaces.length);
1048         for (int i = 0; i < numFaces; i++) {
1049             assertEquals(expectedFaceIds[i], resultFaces[i].getId());
1050             assertEquals(expectedFaceScores[i], resultFaces[i].getScore());
1051             assertEquals(expectedRects[i], resultFaces[i].getBounds());
1052             assertEquals(expectedFaceLMPoints[i*3], resultFaces[i].getLeftEyePosition());
1053             assertEquals(expectedFaceLMPoints[i*3+1], resultFaces[i].getRightEyePosition());
1054             assertEquals(expectedFaceLMPoints[i*3+2], resultFaces[i].getMouthPosition());
1055         }
1056 
1057         /**
1058          * Read - FACE_DETECT_MODE == SIMPLE
1059          */
1060         mMetadata.set(CaptureResult.STATISTICS_FACE_DETECT_MODE,
1061                 CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE);
1062         mMetadata.set(CaptureResult.STATISTICS_FACE_SCORES, expectedFaceScores);
1063         mMetadata.set(CaptureResult.STATISTICS_FACE_RECTANGLES, expectedRects);
1064         Face[] resultSimpleFaces = mMetadata.get(CaptureResult.STATISTICS_FACES);
1065         assertEquals(numFaces, resultSimpleFaces.length);
1066         for (int i = 0; i < numFaces; i++) {
1067             assertEquals(Face.ID_UNSUPPORTED, resultSimpleFaces[i].getId());
1068             assertEquals(expectedFaceScores[i], resultSimpleFaces[i].getScore());
1069             assertEquals(expectedRects[i], resultSimpleFaces[i].getBounds());
1070             assertNull(resultSimpleFaces[i].getLeftEyePosition());
1071             assertNull(resultSimpleFaces[i].getRightEyePosition());
1072             assertNull(resultSimpleFaces[i].getMouthPosition());
1073         }
1074 
1075         /**
1076          * Read/Write TonemapCurve
1077          */
1078         float[] red = new float[] {0.0f, 0.0f, 1.0f, 1.0f};
1079         float[] green = new float[] {0.0f, 1.0f, 1.0f, 0.0f};
1080         float[] blue = new float[] {
1081                 0.0000f, 0.0000f, 0.0667f, 0.2920f, 0.1333f, 0.4002f, 0.2000f, 0.4812f,
1082                 0.2667f, 0.5484f, 0.3333f, 0.6069f, 0.4000f, 0.6594f, 0.4667f, 0.7072f,
1083                 0.5333f, 0.7515f, 0.6000f, 0.7928f, 0.6667f, 0.8317f, 0.7333f, 0.8685f,
1084                 0.8000f, 0.9035f, 0.8667f, 0.9370f, 0.9333f, 0.9691f, 1.0000f, 1.0000f};
1085         TonemapCurve tcIn = new TonemapCurve(red, green, blue);
1086         mMetadata.set(CaptureResult.TONEMAP_CURVE, tcIn);
1087         float[] redOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_RED);
1088         float[] greenOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_GREEN);
1089         float[] blueOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_BLUE);
1090         assertArrayEquals(red, redOut);
1091         assertArrayEquals(green, greenOut);
1092         assertArrayEquals(blue, blueOut);
1093         TonemapCurve tcOut = mMetadata.get(CaptureResult.TONEMAP_CURVE);
1094         assertEquals(tcIn, tcOut);
1095         mMetadata.set(CaptureResult.TONEMAP_CURVE_GREEN, null);
1096         // If any of channel has null curve, return a null TonemapCurve
1097         assertNull(mMetadata.get(CaptureResult.TONEMAP_CURVE));
1098     }
1099 
1100     /**
1101      * Set the raw native value of the available stream configurations; ensure that
1102      * the read-out managed value is consistent with what we write in.
1103      */
1104     @SmallTest
testOverrideStreamConfigurationMap()1105     public void testOverrideStreamConfigurationMap() {
1106 
1107         /*
1108          * First, write all the raw values:
1109          * - availableStreamConfigurations
1110          * - availableMinFrameDurations
1111          * - availableStallDurations
1112          *
1113          * Then, read this out as a synthetic multi-key 'streamConfigurationMap'
1114          *
1115          * Finally, validate that the map was unmarshaled correctly
1116          * and is converting the internal formats to public formats properly.
1117          */
1118 
1119         //
1120         // android.scaler.availableStreamConfigurations (int x n x 4 array)
1121         //
1122         final int OUTPUT = 0;
1123         final int INPUT = 1;
1124         int[] rawAvailableStreamConfigs = new int[] {
1125                 0x20, 3280, 2464, OUTPUT, // RAW16
1126                 0x23, 3264, 2448, OUTPUT, // YCbCr_420_888
1127                 0x23, 3200, 2400, OUTPUT, // YCbCr_420_888
1128                 0x21, 3264, 2448, OUTPUT, // BLOB
1129                 0x21, 3200, 2400, OUTPUT, // BLOB
1130                 0x21, 2592, 1944, OUTPUT, // BLOB
1131                 0x21, 2048, 1536, OUTPUT, // BLOB
1132                 0x21, 1920, 1080, OUTPUT, // BLOB
1133                 0x22, 640, 480, OUTPUT,   // IMPLEMENTATION_DEFINED
1134                 0x20, 320, 240, INPUT,   // RAW16
1135         };
1136         Key<StreamConfiguration[]> configKey =
1137                 CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS.getNativeKey();
1138         mMetadata.writeValues(configKey.getTag(),
1139                 toByteArray(rawAvailableStreamConfigs));
1140 
1141         //
1142         // android.scaler.availableMinFrameDurations (int x n x 4 array)
1143         //
1144         long[] expectedAvailableMinDurations = new long[] {
1145                 0x20, 3280, 2464, 33333331, // RAW16
1146                 0x23, 3264, 2448, 33333332, // YCbCr_420_888
1147                 0x23, 3200, 2400, 33333333, // YCbCr_420_888
1148                 0x100, 3264, 2448, 33333334, // ImageFormat.JPEG
1149                 0x100, 3200, 2400, 33333335, // ImageFormat.JPEG
1150                 0x100, 2592, 1944, 33333336, // ImageFormat.JPEG
1151                 0x100, 2048, 1536, 33333337, // ImageFormat.JPEG
1152                 0x100, 1920, 1080, 33333338  // ImageFormat.JPEG
1153         };
1154         long[] rawAvailableMinDurations = new long[] {
1155                 0x20, 3280, 2464, 33333331, // RAW16
1156                 0x23, 3264, 2448, 33333332, // YCbCr_420_888
1157                 0x23, 3200, 2400, 33333333, // YCbCr_420_888
1158                 0x21, 3264, 2448, 33333334, // BLOB
1159                 0x21, 3200, 2400, 33333335, // BLOB
1160                 0x21, 2592, 1944, 33333336, // BLOB
1161                 0x21, 2048, 1536, 33333337, // BLOB
1162                 0x21, 1920, 1080, 33333338  // BLOB
1163         };
1164         Key<StreamConfigurationDuration[]> durationKey =
1165                 CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS.getNativeKey();
1166         mMetadata.writeValues(durationKey.getTag(),
1167                 toByteArray(rawAvailableMinDurations));
1168 
1169         //
1170         // android.scaler.availableStallDurations (int x n x 4 array)
1171         //
1172         long[] expectedAvailableStallDurations = new long[] {
1173                 0x20, 3280, 2464, 0,        // RAW16
1174                 0x23, 3264, 2448, 0,        // YCbCr_420_888
1175                 0x23, 3200, 2400, 0,        // YCbCr_420_888
1176                 0x100, 3264, 2448, 33333334, // ImageFormat.JPEG
1177                 0x100, 3200, 2400, 33333335, // ImageFormat.JPEG
1178                 0x100, 2592, 1944, 33333336, // ImageFormat.JPEG
1179                 0x100, 2048, 1536, 33333337, // ImageFormat.JPEG
1180                 0x100, 1920, 1080, 33333338  // ImageFormat.JPEG
1181         };
1182         // Note: RAW16 and YUV_420_888 omitted intentionally; omitted values should default to 0
1183         long[] rawAvailableStallDurations = new long[] {
1184                 0x21, 3264, 2448, 33333334, // BLOB
1185                 0x21, 3200, 2400, 33333335, // BLOB
1186                 0x21, 2592, 1944, 33333336, // BLOB
1187                 0x21, 2048, 1536, 33333337, // BLOB
1188                 0x21, 1920, 1080, 33333338  // BLOB
1189         };
1190         Key<StreamConfigurationDuration[]> stallDurationKey =
1191                 CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS.getNativeKey();
1192         mMetadata.writeValues(stallDurationKey.getTag(),
1193                 toByteArray(rawAvailableStallDurations));
1194 
1195         //
1196         // android.scaler.streamConfigurationMap (synthetic as StreamConfigurationMap)
1197         //
1198         StreamConfigurationMap streamConfigMap = mMetadata.get(
1199                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1200 
1201         // Inputs
1202         checkStreamConfigurationMapByFormatSize(
1203                 streamConfigMap, ImageFormat.RAW_SENSOR, 320, 240, /*output*/false);
1204 
1205         // Outputs
1206         checkStreamConfigurationMapByFormatSize(
1207                 streamConfigMap, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, 640, 480, /*output*/true);
1208         checkStreamConfigurationMapByFormatSize(
1209                 streamConfigMap, ImageFormat.JPEG, 1920, 1080, /*output*/true);
1210         checkStreamConfigurationMapByFormatSize(
1211                 streamConfigMap, ImageFormat.JPEG, 2048, 1536, /*output*/true);
1212         checkStreamConfigurationMapByFormatSize(
1213                 streamConfigMap, ImageFormat.JPEG, 2592, 1944, /*output*/true);
1214         checkStreamConfigurationMapByFormatSize(
1215                 streamConfigMap, ImageFormat.JPEG, 3200, 2400, /*output*/true);
1216         checkStreamConfigurationMapByFormatSize(
1217                 streamConfigMap, ImageFormat.YUV_420_888, 3200, 2400, /*output*/true);
1218         checkStreamConfigurationMapByFormatSize(
1219                 streamConfigMap, ImageFormat.YUV_420_888, 3264, 2448, /*output*/true);
1220         checkStreamConfigurationMapByFormatSize(
1221                 streamConfigMap, ImageFormat.RAW_SENSOR, 3280, 2464, /*output*/true);
1222 
1223         // Min Frame Durations
1224 
1225         final int DURATION_TUPLE_SIZE = 4;
1226         for (int i = 0; i < expectedAvailableMinDurations.length; i += DURATION_TUPLE_SIZE) {
1227             checkStreamConfigurationMapDurationByFormatSize(
1228                     streamConfigMap,
1229                     (int)expectedAvailableMinDurations[i],
1230                     (int)expectedAvailableMinDurations[i+1],
1231                     (int)expectedAvailableMinDurations[i+2],
1232                     Duration.MinFrame,
1233                     expectedAvailableMinDurations[i+3]);
1234         }
1235 
1236         // Stall Frame Durations
1237 
1238         for (int i = 0; i < expectedAvailableStallDurations.length; i += DURATION_TUPLE_SIZE) {
1239             checkStreamConfigurationMapDurationByFormatSize(
1240                     streamConfigMap,
1241                     (int)expectedAvailableStallDurations[i],
1242                     (int)expectedAvailableStallDurations[i+1],
1243                     (int)expectedAvailableStallDurations[i+2],
1244                     Duration.Stall,
1245                     expectedAvailableStallDurations[i+3]);
1246         }
1247     }
1248 
assertKeyValueEquals(T expected, CameraCharacteristics.Key<T> key)1249     private <T> void assertKeyValueEquals(T expected, CameraCharacteristics.Key<T> key) {
1250         assertKeyValueEquals(expected, key.getNativeKey());
1251     }
1252 
assertKeyValueEquals(T expected, Key<T> key)1253     private <T> void assertKeyValueEquals(T expected, Key<T> key) {
1254         T actual = mMetadata.get(key);
1255 
1256         assertEquals("Expected value for key " + key + " to match", expected, actual);
1257     }
1258 
1259     @SmallTest
testOverrideMaxRegions()1260     public void testOverrideMaxRegions() {
1261         // All keys are null before doing any writes.
1262         assertKeyValueEquals(null, CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
1263         assertKeyValueEquals(null, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB);
1264         assertKeyValueEquals(null, CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
1265 
1266         mMetadata.set(CameraCharacteristics.CONTROL_MAX_REGIONS,
1267                 new int[] { /*AE*/1, /*AWB*/2, /*AF*/3 });
1268 
1269         // All keys are the expected value after doing a write
1270         assertKeyValueEquals(1, CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
1271         assertKeyValueEquals(2, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB);
1272         assertKeyValueEquals(3, CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
1273     }
1274 
1275     @SmallTest
testOverrideMaxNumOutputStreams()1276     public void testOverrideMaxNumOutputStreams() {
1277         // All keys are null before doing any writes.
1278         assertKeyValueEquals(null, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW);
1279         assertKeyValueEquals(null, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC);
1280         assertKeyValueEquals(null, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING);
1281 
1282         mMetadata.set(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS,
1283                 new int[] { /*AE*/1, /*AWB*/2, /*AF*/3 });
1284 
1285         // All keys are the expected value after doing a write
1286         assertKeyValueEquals(1, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW);
1287         assertKeyValueEquals(2, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC);
1288         assertKeyValueEquals(3, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING);
1289     }
1290 
1291     @SmallTest
testCaptureResult()1292     public void testCaptureResult() {
1293         mMetadata.set(CaptureRequest.CONTROL_AE_MODE,
1294                 CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH);
1295 
1296         if (VERBOSE) mMetadata.dumpToLog();
1297 
1298         CaptureResult captureResult = new CaptureResult(mMetadata, /*sequenceId*/0);
1299 
1300         List<CaptureResult.Key<?>> allKeys = captureResult.getKeys();
1301         if (VERBOSE) Log.v(TAG, "testCaptureResult: key list size " + allKeys);
1302         for (CaptureResult.Key<?> key : captureResult.getKeys()) {
1303             if (VERBOSE) {
1304                 Log.v(TAG,
1305                     "testCaptureResult: key " + key + " value" + captureResult.get(key));
1306             }
1307         }
1308 
1309         assertTrue(allKeys.size() >= 1); // FIXME: android.statistics.faces counts as a key
1310         assertTrue(allKeys.contains(CaptureResult.CONTROL_AE_MODE));
1311 
1312         assertEquals(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH,
1313                 (int)captureResult.get(CaptureResult.CONTROL_AE_MODE));
1314     }
1315 
checkStreamConfigurationMapByFormatSize(StreamConfigurationMap configMap, int format, int width, int height, boolean output)1316     private static void checkStreamConfigurationMapByFormatSize(StreamConfigurationMap configMap,
1317             int format, int width, int height,
1318             boolean output) {
1319 
1320         /** arbitrary class for which StreamConfigurationMap#isOutputSupportedFor(Class) is true */
1321         final Class<?> IMPLEMENTATION_DEFINED_OUTPUT_CLASS = SurfaceTexture.class;
1322 
1323         android.util.Size[] sizes;
1324         int[] formats;
1325 
1326         if (output) {
1327             if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
1328                 sizes = configMap.getOutputSizes(IMPLEMENTATION_DEFINED_OUTPUT_CLASS);
1329                 // in this case the 'is output format supported' is vacuously true
1330                 formats = new int[] { HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED };
1331             } else {
1332                 sizes = configMap.getOutputSizes(format);
1333                 formats = configMap.getOutputFormats();
1334                 assertTrue("Format must be supported by stream configuration map",
1335                         configMap.isOutputSupportedFor(format));
1336             }
1337         } else {
1338             // NOTE: No function to do input sizes from IMPL_DEFINED, so it would just fail for that
1339             sizes = configMap.getInputSizes(format);
1340             formats = configMap.getInputFormats();
1341         }
1342 
1343         android.util.Size expectedSize = new android.util.Size(width, height);
1344 
1345         assertArrayContains(format, formats);
1346         assertArrayContains(expectedSize, sizes);
1347     }
1348 
1349     private enum Duration {
1350         MinFrame,
1351         Stall
1352     }
1353 
checkStreamConfigurationMapDurationByFormatSize( StreamConfigurationMap configMap, int format, int width, int height, Duration durationKind, long expectedDuration)1354     private static void checkStreamConfigurationMapDurationByFormatSize(
1355             StreamConfigurationMap configMap,
1356             int format, int width, int height, Duration durationKind, long expectedDuration) {
1357 
1358         /** arbitrary class for which StreamConfigurationMap#isOutputSupportedFor(Class) is true */
1359         final Class<?> IMPLEMENTATION_DEFINED_OUTPUT_CLASS = SurfaceTexture.class;
1360 
1361         long actualDuration;
1362 
1363         android.util.Size size = new android.util.Size(width, height);
1364         switch (durationKind) {
1365             case MinFrame:
1366                 if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
1367                     actualDuration = configMap.getOutputMinFrameDuration(
1368                             IMPLEMENTATION_DEFINED_OUTPUT_CLASS, size);
1369                 } else {
1370                     actualDuration = configMap.getOutputMinFrameDuration(format, size);
1371                 }
1372 
1373                 break;
1374             case Stall:
1375                 if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
1376                     actualDuration = configMap.getOutputStallDuration(
1377                             IMPLEMENTATION_DEFINED_OUTPUT_CLASS, size);
1378                 } else {
1379                     actualDuration = configMap.getOutputStallDuration(format, size);
1380                 }
1381 
1382                 break;
1383             default:
1384                 throw new AssertionError();
1385         }
1386 
1387         assertEquals("Expected " + durationKind + " to match actual value", expectedDuration,
1388                 actualDuration);
1389     }
1390 
1391     /**
1392      * Validate metadata array tag read/write override.
1393      *
1394      * <p>Only support long and int array for now, can be easily extend to support other
1395      * primitive arrays.</p>
1396      */
validateArrayMetadataReadWriteOverride(Key<T> key, T expectedWriteValues, T expectedReadValues, int tag)1397     private <T> void validateArrayMetadataReadWriteOverride(Key<T> key, T expectedWriteValues,
1398             T expectedReadValues, int tag) {
1399         Class<?> type = expectedWriteValues.getClass();
1400         if (!type.isArray()) {
1401             throw new IllegalArgumentException("This function expects an key with array type");
1402         } else if (type != int[].class && type != long[].class) {
1403             throw new IllegalArgumentException("This function expects long or int array values");
1404         }
1405 
1406         // Write
1407         mMetadata.set(key, expectedWriteValues);
1408 
1409         byte[] readOutValues = mMetadata.readValues(tag);
1410 
1411         ByteBuffer bf = ByteBuffer.wrap(readOutValues).order(ByteOrder.nativeOrder());
1412 
1413         int readValuesLength = Array.getLength(expectedReadValues);
1414         int readValuesNumBytes = readValuesLength * 4;
1415         if (type == long[].class) {
1416             readValuesNumBytes = readValuesLength * 8;
1417         }
1418 
1419         assertEquals(readValuesNumBytes, readOutValues.length);
1420         for (int i = 0; i < readValuesLength; ++i) {
1421             if (type == int[].class) {
1422                 assertEquals(Array.getInt(expectedReadValues, i), bf.getInt());
1423             } else if (type == long[].class) {
1424                 assertEquals(Array.getLong(expectedReadValues, i), bf.getLong());
1425             }
1426         }
1427 
1428         // Read
1429         byte[] readOutValuesAsByteArray = new byte[readValuesNumBytes];
1430         ByteBuffer readOutValuesByteBuffer =
1431                 ByteBuffer.wrap(readOutValuesAsByteArray).order(ByteOrder.nativeOrder());
1432         for (int i = 0; i < readValuesLength; ++i) {
1433             if (type == int[].class) {
1434                 readOutValuesByteBuffer.putInt(Array.getInt(expectedReadValues, i));
1435             } else if (type == long[].class) {
1436                 readOutValuesByteBuffer.putLong(Array.getLong(expectedReadValues, i));
1437             }
1438         }
1439         mMetadata.writeValues(tag, readOutValuesAsByteArray);
1440 
1441         T result = mMetadata.get(key);
1442         assertNotNull(key.getName() + " result shouldn't be null", result);
1443         assertArrayEquals(expectedWriteValues, result);
1444     }
1445 
1446     // TODO: move somewhere else
1447     @SmallTest
testToByteArray()1448     public void testToByteArray() {
1449         assertArrayEquals(new byte[] { 5, 0, 0, 0, 6, 0, 0, 0 },
1450                 toByteArray(5, 6));
1451         assertArrayEquals(new byte[] { 5, 0, 6, 0, },
1452                 toByteArray((short)5, (short)6));
1453         assertArrayEquals(new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
1454                                         (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,},
1455                 toByteArray(~0, ~0));
1456 
1457         assertArrayEquals(new byte[] { (byte)0xAB, (byte)0xFF, 0, 0,
1458                 0x0D, (byte)0xF0, (byte)0xAD, (byte)0xDE },
1459                 toByteArray(0xFFAB, 0xDEADF00D));
1460     }
1461 }
1462