Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 00fb27dd authored by Rachad Alao's avatar Rachad Alao Committed by Android (Google) Code Review
Browse files

Merge "Camera2: add metadata override" into klp-dev

parents a27366ef b7bfdc7c
Loading
Loading
Loading
Loading
+0 −5
Original line number Diff line number Diff line
@@ -60,11 +60,6 @@ public final class CaptureResult extends CameraMetadata {

    @Override
    public <T> T get(Key<T> key) {
        if (key == STATISTICS_FACES) { // Don't throw IllegalArgumentException
            // TODO: Implement android.statistics.faces
            return null;
        }

        return mResults.get(key);
    }

+13 −3
Original line number Diff line number Diff line
@@ -58,6 +58,9 @@ public final class Face {
     * Create a new face with all fields set.
     *
     * <p>The id, leftEyePosition, rightEyePosition, and mouthPosition are considered optional.
     * They are only required when the {@link #CaptureResult} reports that the value of key
     * {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} is
     * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_FULL}.
     * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and
     * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition,
     * rightEyePosition, and mouthPosition may be independently null or not-null.</p>
@@ -107,7 +110,11 @@ public final class Face {
     * <p>The id, leftEyePosition, rightEyePosition, and mouthPosition are considered optional.
     * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and
     * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition,
     * rightEyePosition, and mouthPosition may be independently null or not-null.</p>
     * rightEyePosition, and mouthPosition may be independently null or not-null. When devices
     * report the value of key {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} as
     * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_SIMPLE} in {@link #CaptureResult},
     * the face id of each face is expected to be {@value #ID_UNSUPPORTED}, the leftEyePosition,
     * rightEyePosition, and mouthPositions are expected to be {@code null} for each face.</p>
     *
     * @param bounds Bounds of the face.
     * @param score Confidence level between {@value #SCORE_MIN}-{@value #SCORE_MAX}.
@@ -168,7 +175,10 @@ public final class Face {
     * <p>This is an optional field, may not be supported on all devices.
     * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and
     * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition,
     * rightEyePosition, and mouthPosition may be independently null or not-null.</p>
     * rightEyePosition, and mouthPosition may be independently null or not-null. When devices
     * report the value of key {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} as
     * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_SIMPLE} in {@link #CaptureResult},
     * the face id of each face is expected to be {@value #ID_UNSUPPORTED}.</p>
     *
     * <p>This value will either be {@value #ID_UNSUPPORTED} or
     * otherwise greater than {@code 0}.</p>
@@ -219,7 +229,7 @@ public final class Face {
     * field, may not be supported on all devices. If not
     * supported, the value will always be set to null.
     * This value will  always be null only if {@link #getId()} returns
     * {@value #ID_UNSUPPORTED}.</p> them are.
     * {@value #ID_UNSUPPORTED}.</p>
     * </p>
     *
     * @return The mouth position, or {@code null} if unknown.
+166 −24
Original line number Diff line number Diff line
@@ -16,7 +16,13 @@

package android.hardware.camera2.impl;

import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.Face;
import android.hardware.camera2.Rational;
import android.os.Parcelable;
import android.os.Parcel;
@@ -36,6 +42,8 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable {

    private static final String TAG = "CameraMetadataJV";
    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
    // this should be in sync with HAL_PIXEL_FORMAT_BLOB defined in graphics.h
    private static final int NATIVE_JPEG_FORMAT = 0x21;

    public CameraMetadataNative() {
        super();
@@ -84,16 +92,12 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable {
    @SuppressWarnings("unchecked")
    @Override
    public <T> T get(Key<T> key) {
        int tag = key.getTag();
        byte[] values = readValues(tag);
        if (values == null) {
            return null;
        T value = getOverride(key);
        if (value != null) {
            return value;
        }

        int nativeType = getNativeType(tag);

        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
        return unpackSingle(buffer, key.getType(), nativeType);
        return getBase(key);
    }

    public void readFromParcel(Parcel in) {
@@ -110,24 +114,11 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable {
     * type to the key.
     */
    public <T> void set(Key<T> key, T value) {
        int tag = key.getTag();

        if (value == null) {
            writeValues(tag, null);
        if (setOverride(key, value)) {
            return;
        }

        int nativeType = getNativeType(tag);

        int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true);

        // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
        byte[] values = new byte[size];

        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
        packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false);

        writeValues(tag, values);
        setBase(key, value);
    }

    // Keep up-to-date with camera_metadata.h
@@ -435,6 +426,157 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable {
        return (T) array;
    }

    private <T> T getBase(Key<T> key) {
        int tag = key.getTag();
        byte[] values = readValues(tag);
        if (values == null) {
            return null;
        }

        int nativeType = getNativeType(tag);

        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
        return unpackSingle(buffer, key.getType(), nativeType);
    }

    // Need overwrite some metadata that has different definitions between native
    // and managed sides.
    @SuppressWarnings("unchecked")
    private <T> T getOverride(Key<T> key) {
        if (key == CameraCharacteristics.SCALER_AVAILABLE_FORMATS) {
            return (T) getAvailableFormats();
        } else if (key == CaptureResult.STATISTICS_FACES) {
            return (T) getFaces();
        }

        // For other keys, get() falls back to getBase()
        return null;
    }

    private int[] getAvailableFormats() {
        int[] availableFormats = getBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS);
        for (int i = 0; i < availableFormats.length; i++) {
            // JPEG has different value between native and managed side, need override.
            if (availableFormats[i] == NATIVE_JPEG_FORMAT) {
                availableFormats[i] = ImageFormat.JPEG;
            }
        }
        return availableFormats;
    }

    private Face[] getFaces() {
        final int FACE_LANDMARK_SIZE = 6;

        Integer faceDetectMode = getBase(CaptureResult.STATISTICS_FACE_DETECT_MODE);
        if (faceDetectMode == null) {
            throw new AssertionError("Expect non-null face detect mode");
        }

        if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) {
            return new Face[0];
        }
        if (faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE &&
                faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
            throw new AssertionError("Unknown face detect mode: " + faceDetectMode);
        }

        // Face scores and rectangles are required by SIMPLE and FULL mode.
        byte[] faceScores = getBase(CaptureResult.STATISTICS_FACE_SCORES);
        Rect[] faceRectangles = getBase(CaptureResult.STATISTICS_FACE_RECTANGLES);
        if (faceScores == null || faceRectangles == null) {
            throw new AssertionError("Expect face scores and rectangles to be non-null");
        } else if (faceScores.length != faceRectangles.length) {
            throw new AssertionError(
                    String.format("Face score size(%d) doesn match face rectangle size(%d)!",
                            faceScores.length, faceRectangles.length));
        }

        // Face id and landmarks are only required by FULL mode.
        int[] faceIds = getBase(CaptureResult.STATISTICS_FACE_IDS);
        int[] faceLandmarks = getBase(CaptureResult.STATISTICS_FACE_LANDMARKS);
        int numFaces = faceScores.length;
        if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
            if (faceIds == null || faceLandmarks == null) {
                throw new AssertionError("Expect face ids and landmarks to be non-null for " +
                        "FULL mode");
            } else if (faceIds.length != numFaces ||
                    faceLandmarks.length != numFaces * FACE_LANDMARK_SIZE) {
                throw new AssertionError(
                        String.format("Face id size(%d), or face landmark size(%d) don't match " +
                                "face number(%d)!",
                                faceIds.length, faceLandmarks.length * FACE_LANDMARK_SIZE,
                                numFaces));
            }
        }

        Face[] faces = new Face[numFaces];
        if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE) {
            for (int i = 0; i < numFaces; i++) {
                faces[i] = new Face(faceRectangles[i], faceScores[i]);
            }
        } else {
            // CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL
            for (int i = 0; i < numFaces; i++) {
                Point leftEye = new Point(faceLandmarks[i*6], faceLandmarks[i*6+1]);
                Point rightEye = new Point(faceLandmarks[i*6+2], faceLandmarks[i*6+3]);
                Point mouth = new Point(faceLandmarks[i*6+4], faceLandmarks[i*6+5]);
                faces[i] = new Face(faceRectangles[i], faceScores[i], faceIds[i],
                        leftEye, rightEye, mouth);
            }
        }
        return faces;
    }

    private <T> void setBase(Key<T> key, T value) {
        int tag = key.getTag();

        if (value == null) {
            writeValues(tag, null);
            return;
        }

        int nativeType = getNativeType(tag);

        int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true);

        // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
        byte[] values = new byte[size];

        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
        packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false);

        writeValues(tag, values);
    }

    // Set the camera metadata override.
    private <T> boolean setOverride(Key<T> key, T value) {
        if (key == CameraCharacteristics.SCALER_AVAILABLE_FORMATS) {
            return setAvailableFormats((int[]) value);
        }

        // For other keys, set() falls back to setBase().
        return false;
    }

    private boolean setAvailableFormats(int[] value) {
        int[] availableFormat = value;
        if (value == null) {
            // Let setBase() to handle the null value case.
            return false;
        }

        int[] newValues = new int[availableFormat.length];
        for (int i = 0; i < availableFormat.length; i++) {
            newValues[i] = availableFormat[i];
            if (availableFormat[i] == ImageFormat.JPEG) {
                newValues[i] = NATIVE_JPEG_FORMAT;
            }
        }

        setBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS, newValues);
        return true;
    }

    private long mMetadataPtr; // native CameraMetadata*

    private native long nativeAllocate();
+142 −26
Original line number Diff line number Diff line
@@ -18,9 +18,11 @@ package com.android.mediaframeworktest.unit;

import android.os.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.Face;
import android.hardware.camera2.Rational;
import android.hardware.camera2.Size;
import android.hardware.camera2.impl.CameraMetadataNative;
@@ -30,9 +32,6 @@ import static android.hardware.camera2.impl.CameraMetadataNative.*;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;

import static org.junit.Assert.assertArrayEquals;

/**
 * <pre>
@@ -57,6 +56,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
    // Tags
    static final int ANDROID_COLOR_CORRECTION_MODE = ANDROID_COLOR_CORRECTION_START;
    static final int ANDROID_COLOR_CORRECTION_TRANSFORM = ANDROID_COLOR_CORRECTION_START + 1;
    static final int ANDROID_COLOR_CORRECTION_GAINS = ANDROID_COLOR_CORRECTION_START + 2;

    static final int ANDROID_CONTROL_AE_ANTIBANDING_MODE = ANDROID_CONTROL_START;
    static final int ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION = ANDROID_CONTROL_START + 1;
@@ -131,7 +131,8 @@ public class CameraMetadataTest extends junit.framework.TestCase {
    @SmallTest
    public void testGetTypeFromTag() {
        assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
        assertEquals(TYPE_FLOAT, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
        assertEquals(TYPE_RATIONAL, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
        assertEquals(TYPE_FLOAT, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS));
        assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
        assertEquals(TYPE_INT32,
                CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION));
@@ -187,30 +188,30 @@ public class CameraMetadataTest extends junit.framework.TestCase {
        assertEquals(false, mMetadata.isEmpty());

        //
        // android.colorCorrection.transform (3x3 matrix)
        // android.colorCorrection.colorCorrectionGains (float x 4 array)
        //

        final float[] transformMatrix = new float[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        byte[] transformMatrixAsByteArray = new byte[transformMatrix.length * 4];
        ByteBuffer transformMatrixByteBuffer =
                ByteBuffer.wrap(transformMatrixAsByteArray).order(ByteOrder.nativeOrder());
        for (float f : transformMatrix)
            transformMatrixByteBuffer.putFloat(f);
        final float[] colorCorrectionGains = new float[] { 1.0f, 2.0f, 3.0f, 4.0f};
        byte[] colorCorrectionGainsAsByteArray = new byte[colorCorrectionGains.length * 4];
        ByteBuffer colorCorrectionGainsByteBuffer =
                ByteBuffer.wrap(colorCorrectionGainsAsByteArray).order(ByteOrder.nativeOrder());
        for (float f : colorCorrectionGains)
            colorCorrectionGainsByteBuffer.putFloat(f);

        // Read
        assertNull(mMetadata.readValues(ANDROID_COLOR_CORRECTION_TRANSFORM));
        mMetadata.writeValues(ANDROID_COLOR_CORRECTION_TRANSFORM, transformMatrixAsByteArray);
        assertNull(mMetadata.readValues(ANDROID_COLOR_CORRECTION_GAINS));
        mMetadata.writeValues(ANDROID_COLOR_CORRECTION_GAINS, colorCorrectionGainsAsByteArray);

        // Write
        assertArrayEquals(transformMatrixAsByteArray,
                mMetadata.readValues(ANDROID_COLOR_CORRECTION_TRANSFORM));
        assertArrayEquals(colorCorrectionGainsAsByteArray,
                mMetadata.readValues(ANDROID_COLOR_CORRECTION_GAINS));

        assertEquals(2, mMetadata.getEntryCount());
        assertEquals(false, mMetadata.isEmpty());

        // Erase
        mMetadata.writeValues(ANDROID_COLOR_CORRECTION_TRANSFORM, null);
        assertNull(mMetadata.readValues(ANDROID_COLOR_CORRECTION_TRANSFORM));
        mMetadata.writeValues(ANDROID_COLOR_CORRECTION_GAINS, null);
        assertNull(mMetadata.readValues(ANDROID_COLOR_CORRECTION_GAINS));
        assertEquals(1, mMetadata.getEntryCount());
    }

@@ -279,7 +280,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
    @SmallTest
    public void testReadWritePrimitiveArray() {
        // int32 (n)
        checkKeyGetAndSetArray("android.sensor.info.availableSensitivities", int[].class,
        checkKeyGetAndSetArray("android.sensor.info.sensitivityRange", int[].class,
                new int[] {
                        0xC0FFEE, 0xDEADF00D
                });
@@ -379,7 +380,9 @@ public class CameraMetadataTest extends junit.framework.TestCase {
                new AvailableFormat[] {
                        AvailableFormat.RAW_SENSOR,
                        AvailableFormat.YV12,
                        AvailableFormat.IMPLEMENTATION_DEFINED
                        AvailableFormat.IMPLEMENTATION_DEFINED,
                        AvailableFormat.YCbCr_420_888,
                        AvailableFormat.BLOB
                });

    }
@@ -431,12 +434,13 @@ public class CameraMetadataTest extends junit.framework.TestCase {
                        AvailableFormat.RAW_SENSOR,
                        AvailableFormat.YV12,
                        AvailableFormat.IMPLEMENTATION_DEFINED,
                        AvailableFormat.YCbCr_420_888
                        AvailableFormat.YCbCr_420_888,
                        AvailableFormat.BLOB
                });

        Key<AeAntibandingMode> availableFormatsKey =
                new Key<AeAntibandingMode>("android.scaler.availableFormats",
                        AeAntibandingMode.class);
        Key<AvailableFormat[]> availableFormatsKey =
                new Key<AvailableFormat[]>("android.scaler.availableFormats",
                        AvailableFormat[].class);
        byte[] availableFormatValues = mMetadata.readValues(CameraMetadataNative
                .getTag(availableFormatsKey.getName()));

@@ -444,7 +448,8 @@ public class CameraMetadataTest extends junit.framework.TestCase {
                0x20,
                0x32315659,
                0x22,
                0x23
                0x23,
                0x21
        };

        ByteBuffer bf = ByteBuffer.wrap(availableFormatValues).order(ByteOrder.nativeOrder());
@@ -523,4 +528,115 @@ public class CameraMetadataTest extends junit.framework.TestCase {
    <T> void compareGeneric(T expected, T actual) {
        assertEquals(expected, actual);
    }

    @SmallTest
    public void testReadWriteOverride() {
        //
        // android.scaler.availableFormats (int x n array)
        //
        int[] availableFormats = new int[] {
                0x20,       // RAW_SENSOR
                0x32315659, // YV12
                0x11,       // YCrCb_420_SP
                0x100,      // ImageFormat.JPEG
                0x22,       // IMPLEMENTATION_DEFINED
                0x23,       // YCbCr_420_888
        };
        int[] expectedIntValues = new int[] {
                0x20,       // RAW_SENSOR
                0x32315659, // YV12
                0x11,       // YCrCb_420_SP
                0x21,       // BLOB
                0x22,       // IMPLEMENTATION_DEFINED
                0x23,       // YCbCr_420_888
        };
        int availableFormatTag = CameraMetadataNative.getTag("android.scaler.availableFormats");

        // Write
        mMetadata.set(CameraCharacteristics.SCALER_AVAILABLE_FORMATS, availableFormats);

        byte[] availableFormatValues = mMetadata.readValues(availableFormatTag);

        ByteBuffer bf = ByteBuffer.wrap(availableFormatValues).order(ByteOrder.nativeOrder());

        assertEquals(expectedIntValues.length * 4, availableFormatValues.length);
        for (int i = 0; i < expectedIntValues.length; ++i) {
            assertEquals(expectedIntValues[i], bf.getInt());
        }
        // Read
        byte[] availableFormatsAsByteArray = new byte[expectedIntValues.length * 4];
        ByteBuffer availableFormatsByteBuffer =
                ByteBuffer.wrap(availableFormatsAsByteArray).order(ByteOrder.nativeOrder());
        for (int value : expectedIntValues) {
            availableFormatsByteBuffer.putInt(value);
        }
        mMetadata.writeValues(availableFormatTag, availableFormatsAsByteArray);

        int[] resultFormats = mMetadata.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS);
        assertNotNull("result available formats shouldn't be null", resultFormats);
        assertArrayEquals(availableFormats, resultFormats);

        //
        // android.statistics.faces (Face x n array)
        //
        int[] expectedFaceIds = new int[] {1, 2, 3, 4, 5};
        byte[] expectedFaceScores = new byte[] {10, 20, 30, 40, 50};
        int numFaces = expectedFaceIds.length;
        Rect[] expectedRects = new Rect[numFaces];
        for (int i = 0; i < numFaces; i++) {
            expectedRects[i] = new Rect(i*4 + 1, i * 4 + 2, i * 4 + 3, i * 4 + 4);
        }
        int[] expectedFaceLM = new int[] {
                1, 2, 3, 4, 5, 6,
                7, 8, 9, 10, 11, 12,
                13, 14, 15, 16, 17, 18,
                19, 20, 21, 22, 23, 24,
                25, 26, 27, 28, 29, 30,
        };
        Point[] expectedFaceLMPoints = new Point[numFaces * 3];
        for (int i = 0; i < numFaces; i++) {
            expectedFaceLMPoints[i*3] = new Point(expectedFaceLM[i*6], expectedFaceLM[i*6+1]);
            expectedFaceLMPoints[i*3+1] = new Point(expectedFaceLM[i*6+2], expectedFaceLM[i*6+3]);
            expectedFaceLMPoints[i*3+2] = new Point(expectedFaceLM[i*6+4], expectedFaceLM[i*6+5]);
        }

        /**
         * Read - FACE_DETECT_MODE == FULL
         */
        mMetadata.set(CaptureResult.STATISTICS_FACE_DETECT_MODE,
                CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL);
        mMetadata.set(CaptureResult.STATISTICS_FACE_IDS, expectedFaceIds);
        mMetadata.set(CaptureResult.STATISTICS_FACE_SCORES, expectedFaceScores);
        mMetadata.set(CaptureResult.STATISTICS_FACE_RECTANGLES, expectedRects);
        mMetadata.set(CaptureResult.STATISTICS_FACE_LANDMARKS, expectedFaceLM);
        Face[] resultFaces = mMetadata.get(CaptureResult.STATISTICS_FACES);
        assertEquals(numFaces, resultFaces.length);
        for (int i = 0; i < numFaces; i++) {
            assertEquals(expectedFaceIds[i], resultFaces[i].getId());
            assertEquals(expectedFaceScores[i], resultFaces[i].getScore());
            assertEquals(expectedRects[i], resultFaces[i].getBounds());
            assertEquals(expectedFaceLMPoints[i*3], resultFaces[i].getLeftEyePosition());
            assertEquals(expectedFaceLMPoints[i*3+1], resultFaces[i].getRightEyePosition());
            assertEquals(expectedFaceLMPoints[i*3+2], resultFaces[i].getMouthPosition());
        }

        /**
         * Read - FACE_DETECT_MODE == SIMPLE
         */
        mMetadata.set(CaptureResult.STATISTICS_FACE_DETECT_MODE,
                CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE);
        mMetadata.set(CaptureResult.STATISTICS_FACE_SCORES, expectedFaceScores);
        mMetadata.set(CaptureResult.STATISTICS_FACE_RECTANGLES, expectedRects);
        Face[] resultSimpleFaces = mMetadata.get(CaptureResult.STATISTICS_FACES);
        assertEquals(numFaces, resultSimpleFaces.length);
        for (int i = 0; i < numFaces; i++) {
            assertEquals(Face.ID_UNSUPPORTED, resultSimpleFaces[i].getId());
            assertEquals(expectedFaceScores[i], resultSimpleFaces[i].getScore());
            assertEquals(expectedRects[i], resultSimpleFaces[i].getBounds());
            assertNull(resultSimpleFaces[i].getLeftEyePosition());
            assertNull(resultSimpleFaces[i].getRightEyePosition());
            assertNull(resultSimpleFaces[i].getMouthPosition());
        }

    }
}