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