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));
158        assertEquals(TYPE_RATIONAL,
159                CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
160        assertEquals(TYPE_FLOAT,
161                CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS));
162        assertEquals(TYPE_BYTE,
163                CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
164        assertEquals(TYPE_INT32,
165                CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION));
166
167        try {
168            CameraMetadataNative.getNativeType(0xDEADF00D);
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 4 x 1
735        checkKeyMarshal("android.control.availableHighSpeedVideoConfigurations",
736                new HighSpeedVideoConfiguration(
737                        /*width*/1000, /*height*/255, /*fpsMin*/30, /*fpsMax*/200),
738                /* width, height, fpsMin, fpsMax */
739                toByteArray(1000, 255, 30, 200));
740
741        // int32 x 4 x 3
742        checkKeyMarshal("android.control.availableHighSpeedVideoConfigurations",
743                new HighSpeedVideoConfiguration[] {
744                    new HighSpeedVideoConfiguration(
745                            /*width*/1280, /*height*/720, /*fpsMin*/60, /*fpsMax*/120),
746                    new HighSpeedVideoConfiguration(
747                            /*width*/123, /*height*/456, /*fpsMin*/1, /*fpsMax*/200),
748                    new HighSpeedVideoConfiguration(
749                            /*width*/4096, /*height*/2592, /*fpsMin*/30, /*fpsMax*/60)
750                },
751                toByteArray(
752                        1280, 720, 60, 120,
753                        123, 456, 1, 200,
754                        4096, 2592, 30, 60
755        ));
756    }
757
758    @SmallTest
759    public void testReadWriteColorSpaceTransform() {
760        // rational x 3 x 3
761        checkKeyMarshal("android.colorCorrection.transform",
762                new ColorSpaceTransform(new Rational[] {
763                        new Rational(1, 2), new Rational(3, 4), new Rational(5, 6),
764                        new Rational(7, 8), new Rational(8, 9), new Rational(10, 11),
765                        new Rational(1, 5), new Rational(2, 8), new Rational(3, 9),
766                }),
767                toByteArray(
768                        1, 2, 3, 4, 5, 6,
769                        7, 8, 8, 9, 10, 11,
770                        1, 5, 1, 4, 1, 3));
771    }
772
773    @SmallTest
774    public void testReadWritePoint() {
775        // int32 x 2 [actually 'x n' but pretend it's a single value for now]
776        checkKeyMarshal("android.statistics.hotPixelMap",
777                new Point(1, 2),
778                toByteArray(1, 2));
779
780        // int32 x 2 x samples
781        checkKeyMarshal("android.statistics.hotPixelMap",
782                new Point[] {
783                    new Point(1, 2),
784                    new Point(3, 4),
785                    new Point(5, 6),
786                    new Point(7, 8),
787                },
788                toByteArray(
789                        1, 2,
790                        3, 4,
791                        5, 6,
792                        7, 8)
793        );
794    }
795
796    @SmallTest
797    public void testReadWritePointF() {
798        // float x 2 [actually 'x samples' but pretend it's a single value for now]
799        checkKeyMarshal(
800                "android.sensor.profileToneCurve",
801                new PointF(1.0f, 2.0f),
802                toByteArray(1.0f, 2.0f));
803
804        // float x 2 x samples
805        checkKeyMarshal("android.sensor.profileToneCurve",
806                new PointF[] {
807                    new PointF(1.0f, 2.0f),
808                    new PointF(3.0f, 4.0f),
809                    new PointF(5.0f, 6.0f),
810                    new PointF(7.0f, 8.0f),
811                },
812                toByteArray(
813                        1.0f, 2.0f,
814                        3.0f, 4.0f,
815                        5.0f, 6.0f,
816                        7.0f, 8.0f));
817    }
818
819    @SmallTest
820    public void testReadWritePair() {
821        // float x 2
822        checkKeyMarshal("android.lens.focusRange",
823                new TypeReference<Pair<Float, Float>>() {{ }},
824                Pair.create(1.0f / 2.0f, 1.0f / 3.0f),
825                toByteArray(1.0f / 2.0f, 1.0f / 3.0f));
826
827        // byte, int (fake from TYPE_BYTE)
828        // This takes advantage of the TYPE_BYTE -> int marshaler designed for enums.
829        checkKeyMarshal("android.flash.mode",
830                new TypeReference<Pair<Byte, Integer>>() {{ }},
831                Pair.create((byte)123, 22),
832                toByteArray((byte)123, (byte)22));
833    }
834
835    @SmallTest
836    public void testReadWriteRange() {
837        // int32 x 2
838        checkKeyMarshal("android.control.aeTargetFpsRange",
839                new TypeReference<Range<Integer>>() {{ }},
840                Range.create(123, 456),
841                toByteArray(123, 456));
842
843        // int64 x 2
844        checkKeyMarshal("android.sensor.info.exposureTimeRange",
845                new TypeReference<Range<Long>>() {{ }},
846                Range.create(123L, 456L),
847                toByteArray(123L, 456L));
848    }
849
850    @SmallTest
851    public void testReadWriteStreamConfiguration() {
852        // int32 x 4 x n
853        checkKeyMarshal("android.scaler.availableStreamConfigurations",
854                new StreamConfiguration[] {
855                    new StreamConfiguration(ImageFormat.YUV_420_888, 640, 480, /*input*/false),
856                    new StreamConfiguration(ImageFormat.RGB_565, 320, 240, /*input*/true),
857                },
858                toByteArray(
859                        ImageFormat.YUV_420_888, 640, 480, /*input*/0,
860                        ImageFormat.RGB_565, 320, 240, /*input*/1)
861        );
862    }
863
864    @SmallTest
865    public void testReadWriteStreamConfigurationDuration() {
866        // Avoid sign extending ints when converting to a long
867        final long MASK_UNSIGNED_INT = 0x00000000ffffffffL;
868
869        // int64 x 4 x n
870        checkKeyMarshal("android.scaler.availableMinFrameDurations",
871                new StreamConfigurationDuration[] {
872                    new StreamConfigurationDuration(
873                            ImageFormat.YUV_420_888, 640, 480, /*duration*/123L),
874                    new StreamConfigurationDuration(
875                            ImageFormat.RGB_565, 320, 240, /*duration*/345L),
876                },
877                toByteArray(
878                        ImageFormat.YUV_420_888 & MASK_UNSIGNED_INT, 640L, 480L, /*duration*/123L,
879                        ImageFormat.RGB_565 & MASK_UNSIGNED_INT, 320L, 240L, /*duration*/345L)
880        );
881    }
882
883
884    @SmallTest
885    public void testReadWriteReprocessFormatsMap() {
886
887        // final int RAW_OPAQUE = 0x24; // TODO: add RAW_OPAQUE to ImageFormat
888        final int RAW16 = ImageFormat.RAW_SENSOR;
889        final int YUV_420_888 = ImageFormat.YUV_420_888;
890        final int BLOB = 0x21;
891
892        // TODO: also test HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED as an output
893        int[] contents = new int[] {
894                YUV_420_888, 3, YUV_420_888, ImageFormat.NV21, BLOB,
895                RAW16, 2, YUV_420_888, BLOB,
896
897        };
898
899        // int32 x n
900        Key<ReprocessFormatsMap> key = new Key<ReprocessFormatsMap>(
901                "android.scaler.availableInputOutputFormatsMap", ReprocessFormatsMap.class);
902        mMetadata.writeValues(key.getTag(), toByteArray(contents));
903
904        ReprocessFormatsMap map = mMetadata.get(key);
905
906        /*
907         * Make sure the inputs/outputs were what we expected.
908         * - Use public image format constants here.
909         */
910
911        int[] expectedInputs = new int[] {
912                YUV_420_888, RAW16
913        };
914        assertArrayEquals(expectedInputs, map.getInputs());
915
916        int[] expectedYuvOutputs = new int[] {
917                YUV_420_888, ImageFormat.NV21, ImageFormat.JPEG,
918        };
919        assertArrayEquals(expectedYuvOutputs, map.getOutputs(ImageFormat.YUV_420_888));
920
921        int[] expectedRaw16Outputs = new int[] {
922                YUV_420_888, ImageFormat.JPEG,
923        };
924        assertArrayEquals(expectedRaw16Outputs, map.getOutputs(ImageFormat.RAW_SENSOR));
925
926        // Finally, do a round-trip check as a sanity
927        checkKeyMarshal(
928                "android.scaler.availableInputOutputFormatsMap",
929                new ReprocessFormatsMap(contents),
930                toByteArray(contents)
931        );
932    }
933
934    @SmallTest
935    public void testReadWriteString() {
936        // (byte) string
937        Key<String> gpsProcessingMethodKey =
938                new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
939
940        String helloWorld = new String("HelloWorld");
941        byte[] helloWorldBytes = new byte[] {
942                'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0' };
943
944        mMetadata.set(gpsProcessingMethodKey, helloWorld);
945
946        String actual = mMetadata.get(gpsProcessingMethodKey);
947        assertEquals(helloWorld, actual);
948
949        byte[] actualBytes = mMetadata.readValues(getTag(gpsProcessingMethodKey.getName()));
950        assertArrayEquals(helloWorldBytes, actualBytes);
951
952        // Does not yet test as a string[] since we don't support that in native code.
953
954        // (byte) string
955        Key<String[]> gpsProcessingMethodKeyArray =
956                new Key<String[]>("android.jpeg.gpsProcessingMethod", String[].class);
957
958        String[] gpsStrings = new String[] { "HelloWorld", "FooBar", "Shazbot" };
959        byte[] gpsBytes = new byte[] {
960                'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0',
961                'F', 'o', 'o', 'B', 'a', 'r', '\0',
962                'S', 'h', 'a', 'z', 'b', 'o', 't', '\0'};
963
964        mMetadata.set(gpsProcessingMethodKeyArray, gpsStrings);
965
966        String[] actualArray = mMetadata.get(gpsProcessingMethodKeyArray);
967        assertArrayEquals(gpsStrings, actualArray);
968
969        byte[] actualBytes2 = mMetadata.readValues(getTag(gpsProcessingMethodKeyArray.getName()));
970        assertArrayEquals(gpsBytes, actualBytes2);
971    }
972
973    @SmallTest
974    public void testReadWriteOverride() {
975        //
976        // android.scaler.availableFormats (int x n array)
977        //
978        int[] availableFormats = new int[] {
979                0x20,       // RAW_SENSOR
980                0x32315659, // YV12
981                0x11,       // YCrCb_420_SP
982                0x100,      // ImageFormat.JPEG
983                0x22,       // IMPLEMENTATION_DEFINED
984                0x23,       // YCbCr_420_888
985        };
986        int[] expectedIntValues = new int[] {
987                0x20,       // RAW_SENSOR
988                0x32315659, // YV12
989                0x11,       // YCrCb_420_SP
990                0x21,       // BLOB
991                0x22,       // IMPLEMENTATION_DEFINED
992                0x23,       // YCbCr_420_888
993        };
994        int availableFormatTag = CameraMetadataNative.getTag("android.scaler.availableFormats");
995
996        Key<int[]> formatKey = CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey();
997
998        validateArrayMetadataReadWriteOverride(formatKey, availableFormats,
999                expectedIntValues, availableFormatTag);
1000
1001        //
1002        // android.statistics.faces (Face x n array)
1003        //
1004        int[] expectedFaceIds = new int[] {1, 2, 3, 4, 5};
1005        byte[] expectedFaceScores = new byte[] {10, 20, 30, 40, 50};
1006        int numFaces = expectedFaceIds.length;
1007        Rect[] expectedRects = new Rect[numFaces];
1008        for (int i = 0; i < numFaces; i++) {
1009            expectedRects[i] = new Rect(i*4 + 1, i * 4 + 2, i * 4 + 3, i * 4 + 4);
1010        }
1011        int[] expectedFaceLM = new int[] {
1012                1, 2, 3, 4, 5, 6,
1013                7, 8, 9, 10, 11, 12,
1014                13, 14, 15, 16, 17, 18,
1015                19, 20, 21, 22, 23, 24,
1016                25, 26, 27, 28, 29, 30,
1017        };
1018        Point[] expectedFaceLMPoints = new Point[numFaces * 3];
1019        for (int i = 0; i < numFaces; i++) {
1020            expectedFaceLMPoints[i*3] = new Point(expectedFaceLM[i*6], expectedFaceLM[i*6+1]);
1021            expectedFaceLMPoints[i*3+1] = new Point(expectedFaceLM[i*6+2], expectedFaceLM[i*6+3]);
1022            expectedFaceLMPoints[i*3+2] = new Point(expectedFaceLM[i*6+4], expectedFaceLM[i*6+5]);
1023        }
1024
1025        /**
1026         * Read - FACE_DETECT_MODE == FULL
1027         */
1028        mMetadata.set(CaptureResult.STATISTICS_FACE_DETECT_MODE,
1029                CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL);
1030        mMetadata.set(CaptureResult.STATISTICS_FACE_IDS, expectedFaceIds);
1031        mMetadata.set(CaptureResult.STATISTICS_FACE_SCORES, expectedFaceScores);
1032        mMetadata.set(CaptureResult.STATISTICS_FACE_RECTANGLES, expectedRects);
1033        mMetadata.set(CaptureResult.STATISTICS_FACE_LANDMARKS, expectedFaceLM);
1034        Face[] resultFaces = mMetadata.get(CaptureResult.STATISTICS_FACES);
1035        assertEquals(numFaces, resultFaces.length);
1036        for (int i = 0; i < numFaces; i++) {
1037            assertEquals(expectedFaceIds[i], resultFaces[i].getId());
1038            assertEquals(expectedFaceScores[i], resultFaces[i].getScore());
1039            assertEquals(expectedRects[i], resultFaces[i].getBounds());
1040            assertEquals(expectedFaceLMPoints[i*3], resultFaces[i].getLeftEyePosition());
1041            assertEquals(expectedFaceLMPoints[i*3+1], resultFaces[i].getRightEyePosition());
1042            assertEquals(expectedFaceLMPoints[i*3+2], resultFaces[i].getMouthPosition());
1043        }
1044
1045        /**
1046         * Read - FACE_DETECT_MODE == SIMPLE
1047         */
1048        mMetadata.set(CaptureResult.STATISTICS_FACE_DETECT_MODE,
1049                CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE);
1050        mMetadata.set(CaptureResult.STATISTICS_FACE_SCORES, expectedFaceScores);
1051        mMetadata.set(CaptureResult.STATISTICS_FACE_RECTANGLES, expectedRects);
1052        Face[] resultSimpleFaces = mMetadata.get(CaptureResult.STATISTICS_FACES);
1053        assertEquals(numFaces, resultSimpleFaces.length);
1054        for (int i = 0; i < numFaces; i++) {
1055            assertEquals(Face.ID_UNSUPPORTED, resultSimpleFaces[i].getId());
1056            assertEquals(expectedFaceScores[i], resultSimpleFaces[i].getScore());
1057            assertEquals(expectedRects[i], resultSimpleFaces[i].getBounds());
1058            assertNull(resultSimpleFaces[i].getLeftEyePosition());
1059            assertNull(resultSimpleFaces[i].getRightEyePosition());
1060            assertNull(resultSimpleFaces[i].getMouthPosition());
1061        }
1062
1063        /**
1064         * Read/Write TonemapCurve
1065         */
1066        float[] red = new float[] {0.0f, 0.0f, 1.0f, 1.0f};
1067        float[] green = new float[] {0.0f, 1.0f, 1.0f, 0.0f};
1068        float[] blue = new float[] {
1069                0.0000f, 0.0000f, 0.0667f, 0.2920f, 0.1333f, 0.4002f, 0.2000f, 0.4812f,
1070                0.2667f, 0.5484f, 0.3333f, 0.6069f, 0.4000f, 0.6594f, 0.4667f, 0.7072f,
1071                0.5333f, 0.7515f, 0.6000f, 0.7928f, 0.6667f, 0.8317f, 0.7333f, 0.8685f,
1072                0.8000f, 0.9035f, 0.8667f, 0.9370f, 0.9333f, 0.9691f, 1.0000f, 1.0000f};
1073        TonemapCurve tcIn = new TonemapCurve(red, green, blue);
1074        mMetadata.set(CaptureResult.TONEMAP_CURVE, tcIn);
1075        float[] redOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_RED);
1076        float[] greenOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_GREEN);
1077        float[] blueOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_BLUE);
1078        assertArrayEquals(red, redOut);
1079        assertArrayEquals(green, greenOut);
1080        assertArrayEquals(blue, blueOut);
1081        TonemapCurve tcOut = mMetadata.get(CaptureResult.TONEMAP_CURVE);
1082        assertEquals(tcIn, tcOut);
1083        mMetadata.set(CaptureResult.TONEMAP_CURVE_GREEN, null);
1084        // If any of channel has null curve, return a null TonemapCurve
1085        assertNull(mMetadata.get(CaptureResult.TONEMAP_CURVE));
1086    }
1087
1088    /**
1089     * Set the raw native value of the available stream configurations; ensure that
1090     * the read-out managed value is consistent with what we write in.
1091     */
1092    @SmallTest
1093    public void testOverrideStreamConfigurationMap() {
1094
1095        /*
1096         * First, write all the raw values:
1097         * - availableStreamConfigurations
1098         * - availableMinFrameDurations
1099         * - availableStallDurations
1100         *
1101         * Then, read this out as a synthetic multi-key 'streamConfigurationMap'
1102         *
1103         * Finally, validate that the map was unmarshaled correctly
1104         * and is converting the internal formats to public formats properly.
1105         */
1106
1107        //
1108        // android.scaler.availableStreamConfigurations (int x n x 4 array)
1109        //
1110        final int OUTPUT = 0;
1111        final int INPUT = 1;
1112        int[] rawAvailableStreamConfigs = new int[] {
1113                0x20, 3280, 2464, OUTPUT, // RAW16
1114                0x23, 3264, 2448, OUTPUT, // YCbCr_420_888
1115                0x23, 3200, 2400, OUTPUT, // YCbCr_420_888
1116                0x21, 3264, 2448, OUTPUT, // BLOB
1117                0x21, 3200, 2400, OUTPUT, // BLOB
1118                0x21, 2592, 1944, OUTPUT, // BLOB
1119                0x21, 2048, 1536, OUTPUT, // BLOB
1120                0x21, 1920, 1080, OUTPUT, // BLOB
1121                0x22, 640, 480, OUTPUT,   // IMPLEMENTATION_DEFINED
1122                0x20, 320, 240, INPUT,   // RAW16
1123        };
1124        Key<StreamConfiguration[]> configKey =
1125                CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS.getNativeKey();
1126        mMetadata.writeValues(configKey.getTag(),
1127                toByteArray(rawAvailableStreamConfigs));
1128
1129        //
1130        // android.scaler.availableMinFrameDurations (int x n x 4 array)
1131        //
1132        long[] expectedAvailableMinDurations = new long[] {
1133                0x20, 3280, 2464, 33333331, // RAW16
1134                0x23, 3264, 2448, 33333332, // YCbCr_420_888
1135                0x23, 3200, 2400, 33333333, // YCbCr_420_888
1136                0x100, 3264, 2448, 33333334, // ImageFormat.JPEG
1137                0x100, 3200, 2400, 33333335, // ImageFormat.JPEG
1138                0x100, 2592, 1944, 33333336, // ImageFormat.JPEG
1139                0x100, 2048, 1536, 33333337, // ImageFormat.JPEG
1140                0x100, 1920, 1080, 33333338  // ImageFormat.JPEG
1141        };
1142        long[] rawAvailableMinDurations = new long[] {
1143                0x20, 3280, 2464, 33333331, // RAW16
1144                0x23, 3264, 2448, 33333332, // YCbCr_420_888
1145                0x23, 3200, 2400, 33333333, // YCbCr_420_888
1146                0x21, 3264, 2448, 33333334, // BLOB
1147                0x21, 3200, 2400, 33333335, // BLOB
1148                0x21, 2592, 1944, 33333336, // BLOB
1149                0x21, 2048, 1536, 33333337, // BLOB
1150                0x21, 1920, 1080, 33333338  // BLOB
1151        };
1152        Key<StreamConfigurationDuration[]> durationKey =
1153                CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS.getNativeKey();
1154        mMetadata.writeValues(durationKey.getTag(),
1155                toByteArray(rawAvailableMinDurations));
1156
1157        //
1158        // android.scaler.availableStallDurations (int x n x 4 array)
1159        //
1160        long[] expectedAvailableStallDurations = new long[] {
1161                0x20, 3280, 2464, 0,        // RAW16
1162                0x23, 3264, 2448, 0,        // YCbCr_420_888
1163                0x23, 3200, 2400, 0,        // YCbCr_420_888
1164                0x100, 3264, 2448, 33333334, // ImageFormat.JPEG
1165                0x100, 3200, 2400, 33333335, // ImageFormat.JPEG
1166                0x100, 2592, 1944, 33333336, // ImageFormat.JPEG
1167                0x100, 2048, 1536, 33333337, // ImageFormat.JPEG
1168                0x100, 1920, 1080, 33333338  // ImageFormat.JPEG
1169        };
1170        // Note: RAW16 and YUV_420_888 omitted intentionally; omitted values should default to 0
1171        long[] rawAvailableStallDurations = new long[] {
1172                0x21, 3264, 2448, 33333334, // BLOB
1173                0x21, 3200, 2400, 33333335, // BLOB
1174                0x21, 2592, 1944, 33333336, // BLOB
1175                0x21, 2048, 1536, 33333337, // BLOB
1176                0x21, 1920, 1080, 33333338  // BLOB
1177        };
1178        Key<StreamConfigurationDuration[]> stallDurationKey =
1179                CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS.getNativeKey();
1180        mMetadata.writeValues(stallDurationKey.getTag(),
1181                toByteArray(rawAvailableStallDurations));
1182
1183        //
1184        // android.scaler.streamConfigurationMap (synthetic as StreamConfigurationMap)
1185        //
1186        StreamConfigurationMap streamConfigMap = mMetadata.get(
1187                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1188
1189        // Inputs
1190        checkStreamConfigurationMapByFormatSize(
1191                streamConfigMap, ImageFormat.RAW_SENSOR, 320, 240, /*output*/false);
1192
1193        // Outputs
1194        checkStreamConfigurationMapByFormatSize(
1195                streamConfigMap, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, 640, 480, /*output*/true);
1196        checkStreamConfigurationMapByFormatSize(
1197                streamConfigMap, ImageFormat.JPEG, 1920, 1080, /*output*/true);
1198        checkStreamConfigurationMapByFormatSize(
1199                streamConfigMap, ImageFormat.JPEG, 2048, 1536, /*output*/true);
1200        checkStreamConfigurationMapByFormatSize(
1201                streamConfigMap, ImageFormat.JPEG, 2592, 1944, /*output*/true);
1202        checkStreamConfigurationMapByFormatSize(
1203                streamConfigMap, ImageFormat.JPEG, 3200, 2400, /*output*/true);
1204        checkStreamConfigurationMapByFormatSize(
1205                streamConfigMap, ImageFormat.YUV_420_888, 3200, 2400, /*output*/true);
1206        checkStreamConfigurationMapByFormatSize(
1207                streamConfigMap, ImageFormat.YUV_420_888, 3264, 2448, /*output*/true);
1208        checkStreamConfigurationMapByFormatSize(
1209                streamConfigMap, ImageFormat.RAW_SENSOR, 3280, 2464, /*output*/true);
1210
1211        // Min Frame Durations
1212
1213        final int DURATION_TUPLE_SIZE = 4;
1214        for (int i = 0; i < expectedAvailableMinDurations.length; i += DURATION_TUPLE_SIZE) {
1215            checkStreamConfigurationMapDurationByFormatSize(
1216                    streamConfigMap,
1217                    (int)expectedAvailableMinDurations[i],
1218                    (int)expectedAvailableMinDurations[i+1],
1219                    (int)expectedAvailableMinDurations[i+2],
1220                    Duration.MinFrame,
1221                    expectedAvailableMinDurations[i+3]);
1222        }
1223
1224        // Stall Frame Durations
1225
1226        for (int i = 0; i < expectedAvailableStallDurations.length; i += DURATION_TUPLE_SIZE) {
1227            checkStreamConfigurationMapDurationByFormatSize(
1228                    streamConfigMap,
1229                    (int)expectedAvailableStallDurations[i],
1230                    (int)expectedAvailableStallDurations[i+1],
1231                    (int)expectedAvailableStallDurations[i+2],
1232                    Duration.Stall,
1233                    expectedAvailableStallDurations[i+3]);
1234        }
1235    }
1236
1237    private <T> void assertKeyValueEquals(T expected, CameraCharacteristics.Key<T> key) {
1238        assertKeyValueEquals(expected, key.getNativeKey());
1239    }
1240
1241    private <T> void assertKeyValueEquals(T expected, Key<T> key) {
1242        T actual = mMetadata.get(key);
1243
1244        assertEquals("Expected value for key " + key + " to match", expected, actual);
1245    }
1246
1247    @SmallTest
1248    public void testOverrideMaxRegions() {
1249        // All keys are null before doing any writes.
1250        assertKeyValueEquals(null, CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
1251        assertKeyValueEquals(null, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB);
1252        assertKeyValueEquals(null, CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
1253
1254        mMetadata.set(CameraCharacteristics.CONTROL_MAX_REGIONS,
1255                new int[] { /*AE*/1, /*AWB*/2, /*AF*/3 });
1256
1257        // All keys are the expected value after doing a write
1258        assertKeyValueEquals(1, CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
1259        assertKeyValueEquals(2, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB);
1260        assertKeyValueEquals(3, CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
1261    }
1262
1263    @SmallTest
1264    public void testOverrideMaxNumOutputStreams() {
1265        // All keys are null before doing any writes.
1266        assertKeyValueEquals(null, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW);
1267        assertKeyValueEquals(null, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC);
1268        assertKeyValueEquals(null, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING);
1269
1270        mMetadata.set(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS,
1271                new int[] { /*AE*/1, /*AWB*/2, /*AF*/3 });
1272
1273        // All keys are the expected value after doing a write
1274        assertKeyValueEquals(1, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW);
1275        assertKeyValueEquals(2, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC);
1276        assertKeyValueEquals(3, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING);
1277    }
1278
1279    @SmallTest
1280    public void testCaptureResult() {
1281        mMetadata.set(CaptureRequest.CONTROL_AE_MODE,
1282                CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH);
1283
1284        if (VERBOSE) mMetadata.dumpToLog();
1285
1286        CaptureResult captureResult = new CaptureResult(mMetadata, /*sequenceId*/0);
1287
1288        List<CaptureResult.Key<?>> allKeys = captureResult.getKeys();
1289        if (VERBOSE) Log.v(TAG, "testCaptureResult: key list size " + allKeys);
1290        for (CaptureResult.Key<?> key : captureResult.getKeys()) {
1291            if (VERBOSE) {
1292                Log.v(TAG,
1293                    "testCaptureResult: key " + key + " value" + captureResult.get(key));
1294            }
1295        }
1296
1297        assertTrue(allKeys.size() >= 1); // FIXME: android.statistics.faces counts as a key
1298        assertTrue(allKeys.contains(CaptureResult.CONTROL_AE_MODE));
1299
1300        assertEquals(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH,
1301                (int)captureResult.get(CaptureResult.CONTROL_AE_MODE));
1302    }
1303
1304    private static void checkStreamConfigurationMapByFormatSize(StreamConfigurationMap configMap,
1305            int format, int width, int height,
1306            boolean output) {
1307
1308        /** arbitrary class for which StreamConfigurationMap#isOutputSupportedFor(Class) is true */
1309        final Class<?> IMPLEMENTATION_DEFINED_OUTPUT_CLASS = SurfaceTexture.class;
1310
1311        android.util.Size[] sizes;
1312        int[] formats;
1313
1314        if (output) {
1315            if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
1316                sizes = configMap.getOutputSizes(IMPLEMENTATION_DEFINED_OUTPUT_CLASS);
1317                // in this case the 'is output format supported' is vacuously true
1318                formats = new int[] { HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED };
1319            } else {
1320                sizes = configMap.getOutputSizes(format);
1321                formats = configMap.getOutputFormats();
1322                assertTrue("Format must be supported by stream configuration map",
1323                        configMap.isOutputSupportedFor(format));
1324            }
1325        } else {
1326            // NOTE: No function to do input sizes from IMPL_DEFINED, so it would just fail for that
1327            sizes = configMap.getInputSizes(format);
1328            formats = configMap.getInputFormats();
1329        }
1330
1331        android.util.Size expectedSize = new android.util.Size(width, height);
1332
1333        assertArrayContains(format, formats);
1334        assertArrayContains(expectedSize, sizes);
1335    }
1336
1337    private enum Duration {
1338        MinFrame,
1339        Stall
1340    }
1341
1342    private static void checkStreamConfigurationMapDurationByFormatSize(
1343            StreamConfigurationMap configMap,
1344            int format, int width, int height, Duration durationKind, long expectedDuration) {
1345
1346        /** arbitrary class for which StreamConfigurationMap#isOutputSupportedFor(Class) is true */
1347        final Class<?> IMPLEMENTATION_DEFINED_OUTPUT_CLASS = SurfaceTexture.class;
1348
1349        long actualDuration;
1350
1351        android.util.Size size = new android.util.Size(width, height);
1352        switch (durationKind) {
1353            case MinFrame:
1354                if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
1355                    actualDuration = configMap.getOutputMinFrameDuration(
1356                            IMPLEMENTATION_DEFINED_OUTPUT_CLASS, size);
1357                } else {
1358                    actualDuration = configMap.getOutputMinFrameDuration(format, size);
1359                }
1360
1361                break;
1362            case Stall:
1363                if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
1364                    actualDuration = configMap.getOutputStallDuration(
1365                            IMPLEMENTATION_DEFINED_OUTPUT_CLASS, size);
1366                } else {
1367                    actualDuration = configMap.getOutputStallDuration(format, size);
1368                }
1369
1370                break;
1371            default:
1372                throw new AssertionError();
1373        }
1374
1375        assertEquals("Expected " + durationKind + " to match actual value", expectedDuration,
1376                actualDuration);
1377    }
1378
1379    /**
1380     * Validate metadata array tag read/write override.
1381     *
1382     * <p>Only support long and int array for now, can be easily extend to support other
1383     * primitive arrays.</p>
1384     */
1385    private <T> void validateArrayMetadataReadWriteOverride(Key<T> key, T expectedWriteValues,
1386            T expectedReadValues, int tag) {
1387        Class<?> type = expectedWriteValues.getClass();
1388        if (!type.isArray()) {
1389            throw new IllegalArgumentException("This function expects an key with array type");
1390        } else if (type != int[].class && type != long[].class) {
1391            throw new IllegalArgumentException("This function expects long or int array values");
1392        }
1393
1394        // Write
1395        mMetadata.set(key, expectedWriteValues);
1396
1397        byte[] readOutValues = mMetadata.readValues(tag);
1398
1399        ByteBuffer bf = ByteBuffer.wrap(readOutValues).order(ByteOrder.nativeOrder());
1400
1401        int readValuesLength = Array.getLength(expectedReadValues);
1402        int readValuesNumBytes = readValuesLength * 4;
1403        if (type == long[].class) {
1404            readValuesNumBytes = readValuesLength * 8;
1405        }
1406
1407        assertEquals(readValuesNumBytes, readOutValues.length);
1408        for (int i = 0; i < readValuesLength; ++i) {
1409            if (type == int[].class) {
1410                assertEquals(Array.getInt(expectedReadValues, i), bf.getInt());
1411            } else if (type == long[].class) {
1412                assertEquals(Array.getLong(expectedReadValues, i), bf.getLong());
1413            }
1414        }
1415
1416        // Read
1417        byte[] readOutValuesAsByteArray = new byte[readValuesNumBytes];
1418        ByteBuffer readOutValuesByteBuffer =
1419                ByteBuffer.wrap(readOutValuesAsByteArray).order(ByteOrder.nativeOrder());
1420        for (int i = 0; i < readValuesLength; ++i) {
1421            if (type == int[].class) {
1422                readOutValuesByteBuffer.putInt(Array.getInt(expectedReadValues, i));
1423            } else if (type == long[].class) {
1424                readOutValuesByteBuffer.putLong(Array.getLong(expectedReadValues, i));
1425            }
1426        }
1427        mMetadata.writeValues(tag, readOutValuesAsByteArray);
1428
1429        T result = mMetadata.get(key);
1430        assertNotNull(key.getName() + " result shouldn't be null", result);
1431        assertArrayEquals(expectedWriteValues, result);
1432    }
1433
1434    // TODO: move somewhere else
1435    @SmallTest
1436    public void testToByteArray() {
1437        assertArrayEquals(new byte[] { 5, 0, 0, 0, 6, 0, 0, 0 },
1438                toByteArray(5, 6));
1439        assertArrayEquals(new byte[] { 5, 0, 6, 0, },
1440                toByteArray((short)5, (short)6));
1441        assertArrayEquals(new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
1442                                        (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,},
1443                toByteArray(~0, ~0));
1444
1445        assertArrayEquals(new byte[] { (byte)0xAB, (byte)0xFF, 0, 0,
1446                0x0D, (byte)0xF0, (byte)0xAD, (byte)0xDE },
1447                toByteArray(0xFFAB, 0xDEADF00D));
1448    }
1449}
1450