16d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim/*
26d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Copyright 2016 The Android Open Source Project
36d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim *
46d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Licensed under the Apache License, Version 2.0 (the "License");
56d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * you may not use this file except in compliance with the License.
66d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * You may obtain a copy of the License at
76d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim *
86d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim *      http://www.apache.org/licenses/LICENSE-2.0
96d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim *
106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Unless required by applicable law or agreed to in writing, software
116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * distributed under the License is distributed on an "AS IS" BASIS,
126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * See the License for the specific language governing permissions and
146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * limitations under the License.
156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */
166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimpackage com.android.mediaframeworktest.helpers;
186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport com.android.ex.camera2.blocking.BlockingCameraManager;
206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport com.android.ex.camera2.blocking.BlockingSessionCallback;
226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport com.android.ex.camera2.blocking.BlockingStateCallback;
236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport com.android.ex.camera2.exceptions.TimeoutRuntimeException;
246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport junit.framework.Assert;
266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport org.mockito.Mockito;
286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.graphics.Bitmap;
306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.graphics.BitmapFactory;
316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.graphics.ImageFormat;
326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.graphics.PointF;
336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.graphics.Rect;
346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CameraAccessException;
356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CameraCaptureSession;
366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CameraCharacteristics;
376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession;
386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CameraDevice;
396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CameraManager;
406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CaptureFailure;
416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CaptureRequest;
426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CaptureResult;
436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.TotalCaptureResult;
446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.params.InputConfiguration;
456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.params.MeteringRectangle;
466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.params.StreamConfigurationMap;
476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.location.Location;
486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.location.LocationManager;
496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.media.ExifInterface;
506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.media.Image;
516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.media.Image.Plane;
526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.media.ImageReader;
536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.media.ImageWriter;
546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.os.Build;
556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.os.Environment;
566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.os.Handler;
576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.util.Log;
586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.util.Pair;
596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.util.Size;
606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.view.Display;
616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.view.Surface;
626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.view.WindowManager;
636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.io.FileOutputStream;
656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.io.IOException;
666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.lang.reflect.Array;
676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.nio.ByteBuffer;
686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.text.ParseException;
696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.text.SimpleDateFormat;
706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.ArrayList;
716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.Arrays;
726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.Collections;
736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.Comparator;
746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.Date;
756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.HashMap;
766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.List;
776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.concurrent.LinkedBlockingQueue;
786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.concurrent.Semaphore;
796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.concurrent.TimeUnit;
806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.concurrent.atomic.AtomicLong;
816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim/**
836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * A package private utility class for wrapping up the camera2 framework test common utility
846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * functions
856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */
866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim/**
876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * (non-Javadoc)
886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @see android.hardware.camera2.cts.CameraTestUtils
896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */
906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimpublic class CameraTestUtils extends Assert {
916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static final String TAG = "CameraTestUtils";
926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final Size SIZE_BOUND_1080P = new Size(1920, 1088);
956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final Size SIZE_BOUND_2160P = new Size(3840, 2160);
966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    // Only test the preview size that is no larger than 1080p.
976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final Size PREVIEW_SIZE_BOUND = SIZE_BOUND_1080P;
986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    // Default timeouts for reaching various states
996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int CAMERA_OPEN_TIMEOUT_MS = 3000;
1006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int CAMERA_CLOSE_TIMEOUT_MS = 3000;
1016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int CAMERA_IDLE_TIMEOUT_MS = 3000;
1026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int CAMERA_ACTIVE_TIMEOUT_MS = 1000;
1036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int CAMERA_BUSY_TIMEOUT_MS = 1000;
1046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int CAMERA_UNCONFIGURED_TIMEOUT_MS = 1000;
1056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int CAMERA_CONFIGURE_TIMEOUT_MS = 3000;
1066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int CAPTURE_RESULT_TIMEOUT_MS = 3000;
1076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int CAPTURE_IMAGE_TIMEOUT_MS = 3000;
1086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
1096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int SESSION_CONFIGURE_TIMEOUT_MS = 3000;
1106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int SESSION_CLOSE_TIMEOUT_MS = 3000;
1116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int SESSION_READY_TIMEOUT_MS = 3000;
1126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int SESSION_ACTIVE_TIMEOUT_MS = 1000;
1136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
1146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final int MAX_READER_IMAGES = 5;
1156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
1166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static final int EXIF_DATETIME_LENGTH = 19;
1176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static final int EXIF_DATETIME_ERROR_MARGIN_SEC = 60;
1186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static final float EXIF_FOCAL_LENGTH_ERROR_MARGIN = 0.001f;
1196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static final float EXIF_EXPOSURE_TIME_ERROR_MARGIN_RATIO = 0.05f;
1206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static final float EXIF_EXPOSURE_TIME_MIN_ERROR_MARGIN_SEC = 0.002f;
1216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static final float EXIF_APERTURE_ERROR_MARGIN = 0.001f;
1226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
1236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static final Location sTestLocation0 = new Location(LocationManager.GPS_PROVIDER);
1246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static final Location sTestLocation1 = new Location(LocationManager.GPS_PROVIDER);
1256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static final Location sTestLocation2 = new Location(LocationManager.NETWORK_PROVIDER);
1266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
1276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    protected static final String DEBUG_FILE_NAME_BASE =
1286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Environment.getExternalStorageDirectory().getPath();
1296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
1306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    static {
1316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        sTestLocation0.setTime(1199145600L);
1326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        sTestLocation0.setLatitude(37.736071);
1336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        sTestLocation0.setLongitude(-122.441983);
1346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        sTestLocation0.setAltitude(21.0);
1356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
1366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        sTestLocation1.setTime(1199145601L);
1376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        sTestLocation1.setLatitude(0.736071);
1386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        sTestLocation1.setLongitude(0.441983);
1396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        sTestLocation1.setAltitude(1.0);
1406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
1416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        sTestLocation2.setTime(1199145602L);
1426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        sTestLocation2.setLatitude(-89.736071);
1436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        sTestLocation2.setLongitude(-179.441983);
1446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        sTestLocation2.setAltitude(100000.0);
1456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
1466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
1476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    // Exif test data vectors.
1486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static final ExifTestData[] EXIF_TEST_DATA = {
1496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            new ExifTestData(
1506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    /*gpsLocation*/ sTestLocation0,
1516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    /* orientation */90,
1526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    /* jpgQuality */(byte) 80,
1536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    /* thumbQuality */(byte) 75),
1546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            new ExifTestData(
1556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    /*gpsLocation*/ sTestLocation1,
1566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    /* orientation */180,
1576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    /* jpgQuality */(byte) 90,
1586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    /* thumbQuality */(byte) 85),
1596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            new ExifTestData(
1606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    /*gpsLocation*/ sTestLocation2,
1616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    /* orientation */270,
1626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    /* jpgQuality */(byte) 100,
1636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    /* thumbQuality */(byte) 100)
1646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    };
1656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
1666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
1676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Create an {@link ImageReader} object and get the surface.
1686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
1696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param size The size of this ImageReader to be created.
1706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param format The format of this ImageReader to be created
1716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param maxNumImages The max number of images that can be acquired simultaneously.
1726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param listener The listener used by this ImageReader to notify callbacks.
1736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param handler The handler to use for any listener callbacks.
1746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
1756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static ImageReader makeImageReader(Size size, int format, int maxNumImages,
1766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            ImageReader.OnImageAvailableListener listener, Handler handler) {
1776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        ImageReader reader;
1786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        reader = ImageReader.newInstance(size.getWidth(), size.getHeight(), format,
1796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                maxNumImages);
1806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        reader.setOnImageAvailableListener(listener, handler);
1816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size);
1826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return reader;
1836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
1846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
1856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
1866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Create an ImageWriter and hook up the ImageListener.
1876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
1886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param inputSurface The input surface of the ImageWriter.
1896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param maxImages The max number of Images that can be dequeued simultaneously.
1906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param listener The listener used by this ImageWriter to notify callbacks
1916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param handler The handler to post listener callbacks.
1926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return ImageWriter object created.
1936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
1946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static ImageWriter makeImageWriter(
1956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Surface inputSurface, int maxImages,
1966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            ImageWriter.OnImageReleasedListener listener, Handler handler) {
1976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        ImageWriter writer = ImageWriter.newInstance(inputSurface, maxImages);
1986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        writer.setOnImageReleasedListener(listener, handler);
1996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return writer;
2006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
2016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
2026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
2036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Close pending images and clean up an {@link ImageReader} object.
2046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param reader an {@link ImageReader} to close.
2056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
2066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static void closeImageReader(ImageReader reader) {
2076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (reader != null) {
2086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            reader.close();
2096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
2106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
2116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
2126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
2136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Close pending images and clean up an {@link ImageWriter} object.
2146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param writer an {@link ImageWriter} to close.
2156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
2166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static void closeImageWriter(ImageWriter writer) {
2176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (writer != null) {
2186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            writer.close();
2196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
2206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
2216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
2226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
2236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Dummy listener that release the image immediately once it is available.
2246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
2256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>
2266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * It can be used for the case where we don't care the image data at all.
2276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * </p>
2286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
2296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static class ImageDropperListener implements ImageReader.OnImageAvailableListener {
2306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        @Override
2316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void onImageAvailable(ImageReader reader) {
2326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Image image = null;
2336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            try {
2346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                image = reader.acquireNextImage();
2356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            } finally {
2366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                if (image != null) {
2376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    image.close();
2386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                }
2396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
2406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
2416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
2426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
2436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
2446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Image listener that release the image immediately after validating the image
2456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
2466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static class ImageVerifierListener implements ImageReader.OnImageAvailableListener {
2476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        private Size mSize;
2486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        private int mFormat;
2496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
2506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public ImageVerifierListener(Size sz, int format) {
2516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            mSize = sz;
2526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            mFormat = format;
2536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
2546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
2556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        @Override
2566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void onImageAvailable(ImageReader reader) {
2576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Image image = null;
2586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            try {
2596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                image = reader.acquireNextImage();
2606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            } finally {
2616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                if (image != null) {
2626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    validateImage(image, mSize.getWidth(), mSize.getHeight(), mFormat, null);
2636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    image.close();
2646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                }
2656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
2666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
2676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
2686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
2696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static class SimpleImageReaderListener
2706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            implements ImageReader.OnImageAvailableListener {
2716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        private final LinkedBlockingQueue<Image> mQueue =
2726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                new LinkedBlockingQueue<Image>();
2736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Indicate whether this listener will drop images or not,
2746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // when the queued images reaches the reader maxImages
2756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        private final boolean mAsyncMode;
2766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // maxImages held by the queue in async mode.
2776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        private final int mMaxImages;
2786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
2796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
2806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * Create a synchronous SimpleImageReaderListener that queues the images
2816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * automatically when they are available, no image will be dropped. If
2826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * the caller doesn't call getImage(), the producer will eventually run
2836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * into buffer starvation.
2846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
2856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public SimpleImageReaderListener() {
2866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            mAsyncMode = false;
2876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            mMaxImages = 0;
2886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
2896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
2906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
2916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * Create a synchronous/asynchronous SimpleImageReaderListener that
2926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * queues the images automatically when they are available. For
2936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * asynchronous listener, image will be dropped if the queued images
2946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * reach to maxImages queued. If the caller doesn't call getImage(), the
2956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * producer will not be blocked. For synchronous listener, no image will
2966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * be dropped. If the caller doesn't call getImage(), the producer will
2976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * eventually run into buffer starvation.
2986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
2996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param asyncMode If the listener is operating at asynchronous mode.
3006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param maxImages The max number of images held by this listener.
3016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
3026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
3036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
3046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param asyncMode
3056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
3066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public SimpleImageReaderListener(boolean asyncMode, int maxImages) {
3076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            mAsyncMode = asyncMode;
3086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            mMaxImages = maxImages;
3096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
3106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
3116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        @Override
3126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void onImageAvailable(ImageReader reader) {
3136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            try {
3146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                mQueue.put(reader.acquireNextImage());
3156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                if (mAsyncMode && mQueue.size() >= mMaxImages) {
3166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    Image img = mQueue.poll();
3176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    img.close();
3186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                }
3196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            } catch (InterruptedException e) {
3206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                throw new UnsupportedOperationException(
3216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        "Can't handle InterruptedException in onImageAvailable");
3226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
3236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
3246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
3256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
3266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * Get an image from the image reader.
3276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
3286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param timeout Timeout value for the wait.
3296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @return The image from the image reader.
3306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
3316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public Image getImage(long timeout) throws InterruptedException {
3326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Image image = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
3336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            assertNotNull("Wait for an image timed out in " + timeout + "ms", image);
3346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return image;
3356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
3366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
3376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
3386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * Drain the pending images held by this listener currently.
3396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
3406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
3416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void drain() {
3426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            while (!mQueue.isEmpty()) {
3436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                Image image = mQueue.poll();
3446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                assertNotNull("Unable to get an image", image);
3456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                image.close();
3466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
3476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
3486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
3496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
3506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static class SimpleImageWriterListener implements ImageWriter.OnImageReleasedListener {
3516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        private final Semaphore mImageReleasedSema = new Semaphore(0);
3526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        private final ImageWriter mWriter;
3536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        @Override
3546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void onImageReleased(ImageWriter writer) {
3556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (writer != mWriter) {
3566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                return;
3576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
3586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
3596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (VERBOSE) {
3606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                Log.v(TAG, "Input image is released");
3616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
3626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            mImageReleasedSema.release();
3636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
3646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
3656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public SimpleImageWriterListener(ImageWriter writer) {
3666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (writer == null) {
3676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                throw new IllegalArgumentException("writer cannot be null");
3686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
3696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            mWriter = writer;
3706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
3716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
3726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void waitForImageReleased(long timeoutMs) throws InterruptedException {
3736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (!mImageReleasedSema.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
3746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                fail("wait for image available timed out after " + timeoutMs + "ms");
3756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
3766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
3776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
3786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
3796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static class SimpleCaptureCallback extends CameraCaptureSession.CaptureCallback {
3806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        private final LinkedBlockingQueue<TotalCaptureResult> mQueue =
3816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                new LinkedBlockingQueue<TotalCaptureResult>();
3826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        private final LinkedBlockingQueue<CaptureFailure> mFailureQueue =
3836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                new LinkedBlockingQueue<>();
3846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Pair<CaptureRequest, Long> is a pair of capture request and timestamp.
3856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        private final LinkedBlockingQueue<Pair<CaptureRequest, Long>> mCaptureStartQueue =
3866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                new LinkedBlockingQueue<>();
3876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
3886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        private AtomicLong mNumFramesArrived = new AtomicLong(0);
3896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
3906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        @Override
3916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
3926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                long timestamp, long frameNumber) {
3936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            try {
3946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                mCaptureStartQueue.put(new Pair(request, timestamp));
3956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            } catch (InterruptedException e) {
3966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                throw new UnsupportedOperationException(
3976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        "Can't handle InterruptedException in onCaptureStarted");
3986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
3996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
4006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
4016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        @Override
4026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
4036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                TotalCaptureResult result) {
4046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            try {
4056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                mNumFramesArrived.incrementAndGet();
4066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                mQueue.put(result);
4076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            } catch (InterruptedException e) {
4086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                throw new UnsupportedOperationException(
4096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        "Can't handle InterruptedException in onCaptureCompleted");
4106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
4116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
4126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
4136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        @Override
4146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
4156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                CaptureFailure failure) {
4166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            try {
4176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                mFailureQueue.put(failure);
4186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            } catch (InterruptedException e) {
4196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                throw new UnsupportedOperationException(
4206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        "Can't handle InterruptedException in onCaptureFailed");
4216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
4226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
4236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
4246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        @Override
4256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId,
4266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                long frameNumber) {
4276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
4286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
4296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public long getTotalNumFrames() {
4306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return mNumFramesArrived.get();
4316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
4326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
4336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public CaptureResult getCaptureResult(long timeout) {
4346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return getTotalCaptureResult(timeout);
4356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
4366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
4376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public TotalCaptureResult getCaptureResult(long timeout, long timestamp) {
4386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            try {
4396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                long currentTs = -1L;
4406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                TotalCaptureResult result;
4416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                while (true) {
4426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    result = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
4436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    if (result == null) {
4446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        throw new RuntimeException(
4456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                                "Wait for a capture result timed out in " + timeout + "ms");
4466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    }
4476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    currentTs = result.get(CaptureResult.SENSOR_TIMESTAMP);
4486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    if (currentTs == timestamp) {
4496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        return result;
4506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    }
4516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                }
4526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
4536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            } catch (InterruptedException e) {
4546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                throw new UnsupportedOperationException("Unhandled interrupted exception", e);
4556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
4566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
4576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
4586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public TotalCaptureResult getTotalCaptureResult(long timeout) {
4596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            try {
4606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                TotalCaptureResult result = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
4616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                assertNotNull("Wait for a capture result timed out in " + timeout + "ms", result);
4626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                return result;
4636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            } catch (InterruptedException e) {
4646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                throw new UnsupportedOperationException("Unhandled interrupted exception", e);
4656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
4666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
4676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
4686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
4696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * Get the {@link #CaptureResult capture result} for a given
4706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * {@link #CaptureRequest capture request}.
4716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
4726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param myRequest The {@link #CaptureRequest capture request} whose
4736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            corresponding {@link #CaptureResult capture result} was
4746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            being waited for
4756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param numResultsWait Number of frames to wait for the capture result
4766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            before timeout.
4776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @throws TimeoutRuntimeException If more than numResultsWait results are
4786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            seen before the result matching myRequest arrives, or each
4796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            individual wait for result times out after
4806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            {@value #CAPTURE_RESULT_TIMEOUT_MS}ms.
4816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
4826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public CaptureResult getCaptureResultForRequest(CaptureRequest myRequest,
4836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                int numResultsWait) {
4846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return getTotalCaptureResultForRequest(myRequest, numResultsWait);
4856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
4866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
4876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
4886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * Get the {@link #TotalCaptureResult total capture result} for a given
4896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * {@link #CaptureRequest capture request}.
4906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
4916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param myRequest The {@link #CaptureRequest capture request} whose
4926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            corresponding {@link #TotalCaptureResult capture result} was
4936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            being waited for
4946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param numResultsWait Number of frames to wait for the capture result
4956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            before timeout.
4966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @throws TimeoutRuntimeException If more than numResultsWait results are
4976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            seen before the result matching myRequest arrives, or each
4986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            individual wait for result times out after
4996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            {@value #CAPTURE_RESULT_TIMEOUT_MS}ms.
5006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
5016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public TotalCaptureResult getTotalCaptureResultForRequest(CaptureRequest myRequest,
5026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                int numResultsWait) {
5036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            ArrayList<CaptureRequest> captureRequests = new ArrayList<>(1);
5046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            captureRequests.add(myRequest);
5056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return getTotalCaptureResultsForRequests(captureRequests, numResultsWait)[0];
5066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
5076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
5086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
5096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * Get an array of {@link #TotalCaptureResult total capture results} for a given list of
5106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * {@link #CaptureRequest capture requests}. This can be used when the order of results
5116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * may not the same as the order of requests.
5126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
5136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param captureRequests The list of {@link #CaptureRequest capture requests} whose
5146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            corresponding {@link #TotalCaptureResult capture results} are
5156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            being waited for.
5166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param numResultsWait Number of frames to wait for the capture results
5176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            before timeout.
5186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @throws TimeoutRuntimeException If more than numResultsWait results are
5196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *            seen before all the results matching captureRequests arrives.
5206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
5216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public TotalCaptureResult[] getTotalCaptureResultsForRequests(
5226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                List<CaptureRequest> captureRequests, int numResultsWait) {
5236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (numResultsWait < 0) {
5246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                throw new IllegalArgumentException("numResultsWait must be no less than 0");
5256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
5266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (captureRequests == null || captureRequests.size() == 0) {
5276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                throw new IllegalArgumentException("captureRequests must have at least 1 request.");
5286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
5296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
5306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            // Create a request -> a list of result indices map that it will wait for.
5316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            HashMap<CaptureRequest, ArrayList<Integer>> remainingResultIndicesMap = new HashMap<>();
5326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            for (int i = 0; i < captureRequests.size(); i++) {
5336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                CaptureRequest request = captureRequests.get(i);
5346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                ArrayList<Integer> indices = remainingResultIndicesMap.get(request);
5356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                if (indices == null) {
5366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    indices = new ArrayList<>();
5376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    remainingResultIndicesMap.put(request, indices);
5386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                }
5396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                indices.add(i);
5406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
5416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
5426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            TotalCaptureResult[] results = new TotalCaptureResult[captureRequests.size()];
5436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            int i = 0;
5446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            do {
5456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                TotalCaptureResult result = getTotalCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
5466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                CaptureRequest request = result.getRequest();
5476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                ArrayList<Integer> indices = remainingResultIndicesMap.get(request);
5486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                if (indices != null) {
5496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    results[indices.get(0)] = result;
5506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    indices.remove(0);
5516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
5526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    // Remove the entry if all results for this request has been fulfilled.
5536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    if (indices.isEmpty()) {
5546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        remainingResultIndicesMap.remove(request);
5556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    }
5566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                }
5576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
5586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                if (remainingResultIndicesMap.isEmpty()) {
5596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    return results;
5606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                }
5616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            } while (i++ < numResultsWait);
5626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
5636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new TimeoutRuntimeException("Unable to get the expected capture result after "
5646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    + "waiting for " + numResultsWait + " results");
5656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
5666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
5676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
5686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * Get an array list of {@link #CaptureFailure capture failure} with maxNumFailures entries
5696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * at most. If it times out before maxNumFailures failures are received, return the failures
5706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * received so far.
5716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
5726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param maxNumFailures The maximal number of failures to return. If it times out before
5736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *                       the maximal number of failures are received, return the received
5746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *                       failures so far.
5756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @throws UnsupportedOperationException If an error happens while waiting on the failure.
5766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
5776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public ArrayList<CaptureFailure> getCaptureFailures(long maxNumFailures) {
5786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            ArrayList<CaptureFailure> failures = new ArrayList<>();
5796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            try {
5806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                for (int i = 0; i < maxNumFailures; i++) {
5816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    CaptureFailure failure = mFailureQueue.poll(CAPTURE_RESULT_TIMEOUT_MS,
5826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                            TimeUnit.MILLISECONDS);
5836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    if (failure == null) {
5846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        // If waiting on a failure times out, return the failures so far.
5856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        break;
5866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    }
5876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    failures.add(failure);
5886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                }
5896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }  catch (InterruptedException e) {
5906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                throw new UnsupportedOperationException("Unhandled interrupted exception", e);
5916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
5926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
5936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return failures;
5946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
5956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
5966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
5976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * Wait until the capture start of a request and expected timestamp arrives or it times
5986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * out after a number of capture starts.
5996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
6006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param request The request for the capture start to wait for.
6016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param timestamp The timestamp for the capture start to wait for.
6026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * @param numCaptureStartsWait The number of capture start events to wait for before timing
6036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *                             out.
6046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
6056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void waitForCaptureStart(CaptureRequest request, Long timestamp,
6066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                int numCaptureStartsWait) throws Exception {
6076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Pair<CaptureRequest, Long> expectedShutter = new Pair<>(request, timestamp);
6086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
6096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            int i = 0;
6106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            do {
6116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                Pair<CaptureRequest, Long> shutter = mCaptureStartQueue.poll(
6126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        CAPTURE_RESULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
6136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
6146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                if (shutter == null) {
6156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    throw new TimeoutRuntimeException("Unable to get any more capture start " +
6166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                            "event after waiting for " + CAPTURE_RESULT_TIMEOUT_MS + " ms.");
6176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                } else if (expectedShutter.equals(shutter)) {
6186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    return;
6196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                }
6206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
6216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            } while (i++ < numCaptureStartsWait);
6226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
6236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new TimeoutRuntimeException("Unable to get the expected capture start " +
6246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    "event after waiting for " + numCaptureStartsWait + " capture starts");
6256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
6266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
6276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public boolean hasMoreResults()
6286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        {
6296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return mQueue.isEmpty();
6306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
6316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
6326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void drain() {
6336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            mQueue.clear();
6346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            mNumFramesArrived.getAndSet(0);
6356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            mFailureQueue.clear();
6366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            mCaptureStartQueue.clear();
6376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
6386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
6396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
6406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
6416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Block until the camera is opened.
6426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
6436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>Don't use this to test #onDisconnected/#onError since this will throw
6446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * an AssertionError if it fails to open the camera device.</p>
6456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
6466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return CameraDevice opened camera device
6476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
6486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @throws IllegalArgumentException
6496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *            If the handler is null, or if the handler's looper is current.
6506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @throws CameraAccessException
6516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *            If open fails immediately.
6526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @throws BlockingOpenException
6536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *            If open fails after blocking for some amount of time.
6546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @throws TimeoutRuntimeException
6556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *            If opening times out. Typically unrecoverable.
6566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
6576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static CameraDevice openCamera(CameraManager manager, String cameraId,
6586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraDevice.StateCallback listener, Handler handler) throws CameraAccessException,
6596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            BlockingOpenException {
6606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
6616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
6626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * Although camera2 API allows 'null' Handler (it will just use the current
6636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * thread's Looper), this is not what we want for CTS.
6646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
6656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * In Camera framework test the default looper is used only to process events
6666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * in between test runs,
6676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * so anything sent there would not be executed inside a test and the test would fail.
6686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
6696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * In this case, BlockingCameraManager#openCamera performs the check for us.
6706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
6716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return (new BlockingCameraManager(manager)).openCamera(cameraId, listener, handler);
6726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
6736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
6746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
6756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
6766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Block until the camera is opened.
6776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
6786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>Don't use this to test #onDisconnected/#onError since this will throw
6796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * an AssertionError if it fails to open the camera device.</p>
6806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
6816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @throws IllegalArgumentException
6826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *            If the handler is null, or if the handler's looper is current.
6836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @throws CameraAccessException
6846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *            If open fails immediately.
6856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @throws BlockingOpenException
6866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *            If open fails after blocking for some amount of time.
6876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @throws TimeoutRuntimeException
6886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *            If opening times out. Typically unrecoverable.
6896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
6906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static CameraDevice openCamera(CameraManager manager, String cameraId, Handler handler)
6916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throws CameraAccessException,
6926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            BlockingOpenException {
6936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return openCamera(manager, cameraId, /*listener*/null, handler);
6946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
6956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
6966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
6976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Configure a new camera session with output surfaces and type.
6986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
6996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param camera The CameraDevice to be configured.
7006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param outputSurfaces The surface list that used for camera output.
7016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param listener The callback CameraDevice will notify when capture results are available.
7026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
7036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static CameraCaptureSession configureCameraSession(CameraDevice camera,
7046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            List<Surface> outputSurfaces, boolean isHighSpeed,
7056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraCaptureSession.StateCallback listener, Handler handler)
7066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throws CameraAccessException {
7076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        BlockingSessionCallback sessionListener = new BlockingSessionCallback(listener);
7086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (isHighSpeed) {
7096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            camera.createConstrainedHighSpeedCaptureSession(outputSurfaces,
7106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    sessionListener, handler);
7116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        } else {
7126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            camera.createCaptureSession(outputSurfaces, sessionListener, handler);
7136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
7146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        CameraCaptureSession session =
7156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                sessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
7166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertFalse("Camera session should not be a reprocessable session",
7176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                session.isReprocessable());
7186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        String sessionType = isHighSpeed ? "High Speed" : "Normal";
7196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertTrue("Capture session type must be " + sessionType,
7206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                isHighSpeed ==
7216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                CameraConstrainedHighSpeedCaptureSession.class.isAssignableFrom(session.getClass()));
7226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
7236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return session;
7246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
7256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
7266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
7276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Configure a new camera session with output surfaces.
7286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
7296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param camera The CameraDevice to be configured.
7306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param outputSurfaces The surface list that used for camera output.
7316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param listener The callback CameraDevice will notify when capture results are available.
7326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
7336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static CameraCaptureSession configureCameraSession(CameraDevice camera,
7346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            List<Surface> outputSurfaces,
7356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraCaptureSession.StateCallback listener, Handler handler)
7366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throws CameraAccessException {
7376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
7386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return configureCameraSession(camera, outputSurfaces, /*isHighSpeed*/false,
7396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                listener, handler);
7406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
7416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
7426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static CameraCaptureSession configureReprocessableCameraSession(CameraDevice camera,
7436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            InputConfiguration inputConfiguration, List<Surface> outputSurfaces,
7446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraCaptureSession.StateCallback listener, Handler handler)
7456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throws CameraAccessException {
7466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        BlockingSessionCallback sessionListener = new BlockingSessionCallback(listener);
7476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        camera.createReprocessableCaptureSession(inputConfiguration, outputSurfaces,
7486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                sessionListener, handler);
7496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
7506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Integer[] sessionStates = {BlockingSessionCallback.SESSION_READY,
7516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                                   BlockingSessionCallback.SESSION_CONFIGURE_FAILED};
7526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int state = sessionListener.getStateWaiter().waitForAnyOfStates(
7536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                Arrays.asList(sessionStates), SESSION_CONFIGURE_TIMEOUT_MS);
7546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
7556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertTrue("Creating a reprocessable session failed.",
7566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                state == BlockingSessionCallback.SESSION_READY);
7576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
7586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        CameraCaptureSession session =
7596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                sessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
7606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertTrue("Camera session should be a reprocessable session", session.isReprocessable());
7616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
7626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return session;
7636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
7646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
7656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static <T> void assertArrayNotEmpty(T arr, String message) {
7666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertTrue(message, arr != null && Array.getLength(arr) > 0);
7676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
7686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
7696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
7706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Check if the format is a legal YUV format camera supported.
7716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
7726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static void checkYuvFormat(int format) {
7736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if ((format != ImageFormat.YUV_420_888) &&
7746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                (format != ImageFormat.NV21) &&
7756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                (format != ImageFormat.YV12)) {
7766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            fail("Wrong formats: " + format);
7776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
7786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
7796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
7806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
7816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Check if image size and format match given size and format.
7826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
7836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static void checkImage(Image image, int width, int height, int format) {
7846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Image reader will wrap YV12/NV21 image by YUV_420_888
7856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (format == ImageFormat.NV21 || format == ImageFormat.YV12) {
7866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            format = ImageFormat.YUV_420_888;
7876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
7886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertNotNull("Input image is invalid", image);
7896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertEquals("Format doesn't match", format, image.getFormat());
7906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertEquals("Width doesn't match", width, image.getWidth());
7916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertEquals("Height doesn't match", height, image.getHeight());
7926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
7936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
7946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
7956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>Read data from all planes of an Image into a contiguous unpadded, unpacked
7966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * 1-D linear byte array, such that it can be write into disk, or accessed by
7976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * software conveniently. It supports YUV_420_888/NV21/YV12 and JPEG input
7986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Image format.</p>
7996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
8006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>For YUV_420_888/NV21/YV12/Y8/Y16, it returns a byte array that contains
8016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * the Y plane data first, followed by U(Cb), V(Cr) planes if there is any
8026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * (xstride = width, ystride = height for chroma and luma components).</p>
8036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
8046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>For JPEG, it returns a 1-D byte array contains a complete JPEG image.</p>
8056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
8066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static byte[] getDataFromImage(Image image) {
8076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertNotNull("Invalid image:", image);
8086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int format = image.getFormat();
8096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int width = image.getWidth();
8106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int height = image.getHeight();
8116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int rowStride, pixelStride;
8126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        byte[] data = null;
8136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
8146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Read image data
8156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Plane[] planes = image.getPlanes();
8166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertTrue("Fail to get image planes", planes != null && planes.length > 0);
8176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
8186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Check image validity
8196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        checkAndroidImageFormat(image);
8206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
8216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        ByteBuffer buffer = null;
8226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // JPEG doesn't have pixelstride and rowstride, treat it as 1D buffer.
8236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Same goes for DEPTH_POINT_CLOUD
8246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (format == ImageFormat.JPEG || format == ImageFormat.DEPTH_POINT_CLOUD ||
8256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                format == ImageFormat.RAW_PRIVATE) {
8266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            buffer = planes[0].getBuffer();
8276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            assertNotNull("Fail to get jpeg or depth ByteBuffer", buffer);
8286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            data = new byte[buffer.remaining()];
8296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            buffer.get(data);
8306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            buffer.rewind();
8316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return data;
8326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
8336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
8346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int offset = 0;
8356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8];
8366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int maxRowSize = planes[0].getRowStride();
8376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        for (int i = 0; i < planes.length; i++) {
8386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (maxRowSize < planes[i].getRowStride()) {
8396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                maxRowSize = planes[i].getRowStride();
8406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
8416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
8426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        byte[] rowData = new byte[maxRowSize];
8436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if(VERBOSE) Log.v(TAG, "get data from " + planes.length + " planes");
8446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        for (int i = 0; i < planes.length; i++) {
8456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            buffer = planes[i].getBuffer();
8466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            assertNotNull("Fail to get bytebuffer from plane", buffer);
8476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            rowStride = planes[i].getRowStride();
8486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            pixelStride = planes[i].getPixelStride();
8496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            assertTrue("pixel stride " + pixelStride + " is invalid", pixelStride > 0);
8506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (VERBOSE) {
8516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                Log.v(TAG, "pixelStride " + pixelStride);
8526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                Log.v(TAG, "rowStride " + rowStride);
8536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                Log.v(TAG, "width " + width);
8546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                Log.v(TAG, "height " + height);
8556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
8566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            // For multi-planar yuv images, assuming yuv420 with 2x2 chroma subsampling.
8576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            int w = (i == 0) ? width : width / 2;
8586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            int h = (i == 0) ? height : height / 2;
8596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            assertTrue("rowStride " + rowStride + " should be >= width " + w , rowStride >= w);
8606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            for (int row = 0; row < h; row++) {
8616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8;
8626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                int length;
8636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                if (pixelStride == bytesPerPixel) {
8646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    // Special case: optimized read of the entire row
8656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    length = w * bytesPerPixel;
8666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    buffer.get(data, offset, length);
8676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    offset += length;
8686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                } else {
8696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    // Generic case: should work for any pixelStride but slower.
8706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    // Use intermediate buffer to avoid read byte-by-byte from
8716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    // DirectByteBuffer, which is very bad for performance
8726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    length = (w - 1) * pixelStride + bytesPerPixel;
8736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    buffer.get(rowData, 0, length);
8746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    for (int col = 0; col < w; col++) {
8756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        data[offset++] = rowData[col * pixelStride];
8766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    }
8776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                }
8786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                // Advance buffer the remainder of the row stride
8796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                if (row < h - 1) {
8806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    buffer.position(buffer.position() + rowStride - length);
8816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                }
8826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
8836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (VERBOSE) Log.v(TAG, "Finished reading data from plane " + i);
8846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            buffer.rewind();
8856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
8866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return data;
8876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
8886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
8896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
8906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>Check android image format validity for an image, only support below formats:</p>
8916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
8926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>YUV_420_888/NV21/YV12, can add more for future</p>
8936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
8946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static void checkAndroidImageFormat(Image image) {
8956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int format = image.getFormat();
8966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Plane[] planes = image.getPlanes();
8976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        switch (format) {
8986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.YUV_420_888:
8996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.NV21:
9006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.YV12:
9016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                assertEquals("YUV420 format Images should have 3 planes", 3, planes.length);
9026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                break;
9036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.JPEG:
9046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.RAW_SENSOR:
9056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.RAW_PRIVATE:
9066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.DEPTH16:
9076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.DEPTH_POINT_CLOUD:
9086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                assertEquals("JPEG/RAW/depth Images should have one plane", 1, planes.length);
9096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                break;
9106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            default:
9116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                fail("Unsupported Image Format: " + format);
9126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
9136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
9146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
9156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static void dumpFile(String fileName, Bitmap data) {
9166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        FileOutputStream outStream;
9176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        try {
9186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Log.v(TAG, "output will be saved as " + fileName);
9196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            outStream = new FileOutputStream(fileName);
9206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        } catch (IOException ioe) {
9216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new RuntimeException("Unable to create debug output file " + fileName, ioe);
9226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
9236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
9246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        try {
9256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            data.compress(Bitmap.CompressFormat.JPEG, /*quality*/90, outStream);
9266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            outStream.close();
9276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        } catch (IOException ioe) {
9286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new RuntimeException("failed writing data to file " + fileName, ioe);
9296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
9306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
9316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
9326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static void dumpFile(String fileName, byte[] data) {
9336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        FileOutputStream outStream;
9346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        try {
9356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Log.v(TAG, "output will be saved as " + fileName);
9366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            outStream = new FileOutputStream(fileName);
9376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        } catch (IOException ioe) {
9386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new RuntimeException("Unable to create debug output file " + fileName, ioe);
9396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
9406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
9416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        try {
9426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            outStream.write(data);
9436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            outStream.close();
9446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        } catch (IOException ioe) {
9456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new RuntimeException("failed writing data to file " + fileName, ioe);
9466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
9476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
9486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
9496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
9506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get the available output sizes for the user-defined {@code format}.
9516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
9526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>Note that implementation-defined/hidden formats are not supported.</p>
9536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
9546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static Size[] getSupportedSizeForFormat(int format, String cameraId,
9556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraManager cameraManager) throws CameraAccessException {
9566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        CameraCharacteristics properties = cameraManager.getCameraCharacteristics(cameraId);
9576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertNotNull("Can't get camera characteristics!", properties);
9586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (VERBOSE) {
9596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Log.v(TAG, "get camera characteristics for camera: " + cameraId);
9606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
9616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        StreamConfigurationMap configMap =
9626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                properties.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
9636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Size[] availableSizes = configMap.getOutputSizes(format);
9646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertArrayNotEmpty(availableSizes, "availableSizes should not be empty for format: "
9656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                + format);
9666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Size[] highResAvailableSizes = configMap.getHighResolutionOutputSizes(format);
9676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (highResAvailableSizes != null && highResAvailableSizes.length > 0) {
9686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Size[] allSizes = new Size[availableSizes.length + highResAvailableSizes.length];
9696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            System.arraycopy(availableSizes, 0, allSizes, 0,
9706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    availableSizes.length);
9716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            System.arraycopy(highResAvailableSizes, 0, allSizes, availableSizes.length,
9726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    highResAvailableSizes.length);
9736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            availableSizes = allSizes;
9746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
9756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (VERBOSE) Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(availableSizes));
9766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return availableSizes;
9776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
9786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
9796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
9806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get the available output sizes for the given class.
9816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
9826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
9836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static Size[] getSupportedSizeForClass(Class klass, String cameraId,
9846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraManager cameraManager) throws CameraAccessException {
9856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        CameraCharacteristics properties = cameraManager.getCameraCharacteristics(cameraId);
9866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertNotNull("Can't get camera characteristics!", properties);
9876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (VERBOSE) {
9886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Log.v(TAG, "get camera characteristics for camera: " + cameraId);
9896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
9906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        StreamConfigurationMap configMap =
9916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                properties.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
9926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Size[] availableSizes = configMap.getOutputSizes(klass);
9936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertArrayNotEmpty(availableSizes, "availableSizes should not be empty for class: "
9946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                + klass);
9956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Size[] highResAvailableSizes = configMap.getHighResolutionOutputSizes(ImageFormat.PRIVATE);
9966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (highResAvailableSizes != null && highResAvailableSizes.length > 0) {
9976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Size[] allSizes = new Size[availableSizes.length + highResAvailableSizes.length];
9986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            System.arraycopy(availableSizes, 0, allSizes, 0,
9996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    availableSizes.length);
10006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            System.arraycopy(highResAvailableSizes, 0, allSizes, availableSizes.length,
10016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    highResAvailableSizes.length);
10026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            availableSizes = allSizes;
10036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
10046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (VERBOSE) Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(availableSizes));
10056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return availableSizes;
10066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
10076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
10086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
10096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Size comparator that compares the number of pixels it covers.
10106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
10116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>If two the areas of two sizes are same, compare the widths.</p>
10126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
10136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static class SizeComparator implements Comparator<Size> {
10146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        @Override
10156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public int compare(Size lhs, Size rhs) {
10166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return CameraUtils
10176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    .compareSizes(lhs.getWidth(), lhs.getHeight(), rhs.getWidth(), rhs.getHeight());
10186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
10196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
10206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
10216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
10226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get sorted size list in descending order. Remove the sizes larger than
10236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * the bound. If the bound is null, don't do the size bound filtering.
10246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
10256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    static public List<Size> getSupportedPreviewSizes(String cameraId,
10266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraManager cameraManager, Size bound) throws CameraAccessException {
10276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
10286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Size[] rawSizes = getSupportedSizeForClass(android.view.SurfaceHolder.class, cameraId,
10296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                cameraManager);
10306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertArrayNotEmpty(rawSizes,
10316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                "Available sizes for SurfaceHolder class should not be empty");
10326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (VERBOSE) {
10336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(rawSizes));
10346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
10356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
10366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (bound == null) {
10376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return getAscendingOrderSizes(Arrays.asList(rawSizes), /*ascending*/false);
10386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
10396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
10406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        List<Size> sizes = new ArrayList<Size>();
10416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        for (Size sz: rawSizes) {
10426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (sz.getWidth() <= bound.getWidth() && sz.getHeight() <= bound.getHeight()) {
10436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                sizes.add(sz);
10446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
10456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
10466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return getAscendingOrderSizes(sizes, /*ascending*/false);
10476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
10486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
10496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
10506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get a sorted list of sizes from a given size list.
10516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
10526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>
10536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * The size is compare by area it covers, if the areas are same, then
10546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * compare the widths.
10556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * </p>
10566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
10576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param sizeList The input size list to be sorted
10586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param ascending True if the order is ascending, otherwise descending order
10596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return The ordered list of sizes
10606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
10616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    static public List<Size> getAscendingOrderSizes(final List<Size> sizeList, boolean ascending) {
10626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (sizeList == null) {
10636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("sizeList shouldn't be null");
10646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
10656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
10666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Comparator<Size> comparator = new SizeComparator();
10676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        List<Size> sortedSizes = new ArrayList<Size>();
10686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        sortedSizes.addAll(sizeList);
10696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Collections.sort(sortedSizes, comparator);
10706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (!ascending) {
10716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Collections.reverse(sortedSizes);
10726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
10736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
10746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return sortedSizes;
10756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
10766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
10776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
10786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get sorted (descending order) size list for given format. Remove the sizes larger than
10796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * the bound. If the bound is null, don't do the size bound filtering.
10806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
10816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    static public List<Size> getSortedSizesForFormat(String cameraId,
10826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraManager cameraManager, int format, Size bound) throws CameraAccessException {
10836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Comparator<Size> comparator = new SizeComparator();
10846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Size[] sizes = getSupportedSizeForFormat(format, cameraId, cameraManager);
10856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        List<Size> sortedSizes = null;
10866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (bound != null) {
10876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            sortedSizes = new ArrayList<Size>(/*capacity*/1);
10886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            for (Size sz : sizes) {
10896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                if (comparator.compare(sz, bound) <= 0) {
10906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    sortedSizes.add(sz);
10916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                }
10926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
10936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        } else {
10946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            sortedSizes = Arrays.asList(sizes);
10956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
10966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertTrue("Supported size list should have at least one element",
10976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                sortedSizes.size() > 0);
10986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
10996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Collections.sort(sortedSizes, comparator);
11006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Make it in descending order.
11016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Collections.reverse(sortedSizes);
11026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return sortedSizes;
11036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
11046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
11056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
11066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get supported video size list for a given camera device.
11076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
11086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>
11096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Filter out the sizes that are larger than the bound. If the bound is
11106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * null, don't do the size bound filtering.
11116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * </p>
11126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
11136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    static public List<Size> getSupportedVideoSizes(String cameraId,
11146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraManager cameraManager, Size bound) throws CameraAccessException {
11156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
11166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Size[] rawSizes = getSupportedSizeForClass(android.media.MediaRecorder.class,
11176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                cameraId, cameraManager);
11186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertArrayNotEmpty(rawSizes,
11196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                "Available sizes for MediaRecorder class should not be empty");
11206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (VERBOSE) {
11216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(rawSizes));
11226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
11236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
11246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (bound == null) {
11256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return getAscendingOrderSizes(Arrays.asList(rawSizes), /*ascending*/false);
11266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
11276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
11286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        List<Size> sizes = new ArrayList<Size>();
11296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        for (Size sz: rawSizes) {
11306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (sz.getWidth() <= bound.getWidth() && sz.getHeight() <= bound.getHeight()) {
11316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                sizes.add(sz);
11326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
11336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
11346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return getAscendingOrderSizes(sizes, /*ascending*/false);
11356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
11366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
11376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
11386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get supported video size list (descending order) for a given camera device.
11396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
11406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>
11416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Filter out the sizes that are larger than the bound. If the bound is
11426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * null, don't do the size bound filtering.
11436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * </p>
11446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
11456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    static public List<Size> getSupportedStillSizes(String cameraId,
11466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraManager cameraManager, Size bound) throws CameraAccessException {
11476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.JPEG, bound);
11486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
11496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
11506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    static public Size getMinPreviewSize(String cameraId, CameraManager cameraManager)
11516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throws CameraAccessException {
11526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        List<Size> sizes = getSupportedPreviewSizes(cameraId, cameraManager, null);
11536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return sizes.get(sizes.size() - 1);
11546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
11556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
11566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
11576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get max supported preview size for a camera device.
11586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
11596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    static public Size getMaxPreviewSize(String cameraId, CameraManager cameraManager)
11606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throws CameraAccessException {
11616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return getMaxPreviewSize(cameraId, cameraManager, /*bound*/null);
11626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
11636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
11646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
11656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get max preview size for a camera device in the supported sizes that are no larger
11666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * than the bound.
11676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
11686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    static public Size getMaxPreviewSize(String cameraId, CameraManager cameraManager, Size bound)
11696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throws CameraAccessException {
11706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        List<Size> sizes = getSupportedPreviewSizes(cameraId, cameraManager, bound);
11716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return sizes.get(0);
11726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
11736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
11746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
11756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get max depth size for a camera device.
11766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
11776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    static public Size getMaxDepthSize(String cameraId, CameraManager cameraManager)
11786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throws CameraAccessException {
11796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        List<Size> sizes = getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.DEPTH16,
11806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                /*bound*/ null);
11816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return sizes.get(0);
11826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
11836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
11846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
11856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get the largest size by area.
11866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
11876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param sizes an array of sizes, must have at least 1 element
11886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
11896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return Largest Size
11906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
11916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @throws IllegalArgumentException if sizes was null or had 0 elements
11926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
11936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static Size getMaxSize(Size... sizes) {
11946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (sizes == null || sizes.length == 0) {
11956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("sizes was empty");
11966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
11976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
11986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Size sz = sizes[0];
11996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        for (Size size : sizes) {
12006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (size.getWidth() * size.getHeight() > sz.getWidth() * sz.getHeight()) {
12016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                sz = size;
12026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
12036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
12046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
12056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return sz;
12066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
12076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
12086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
12096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Returns true if the given {@code array} contains the given element.
12106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
12116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param array {@code array} to check for {@code elem}
12126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param elem {@code elem} to test for
12136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return {@code true} if the given element is contained
12146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
12156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static boolean contains(int[] array, int elem) {
12166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (array == null) return false;
12176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        for (int i = 0; i < array.length; i++) {
12186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (elem == array[i]) return true;
12196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
12206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return false;
12216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
12226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
12236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
12246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get object array from byte array.
12256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
12266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param array Input byte array to be converted
12276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return Byte object array converted from input byte array
12286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
12296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static Byte[] toObject(byte[] array) {
12306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return convertPrimitiveArrayToObjectArray(array, Byte.class);
12316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
12326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
12336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
12346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get object array from int array.
12356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
12366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param array Input int array to be converted
12376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return Integer object array converted from input int array
12386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
12396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static Integer[] toObject(int[] array) {
12406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return convertPrimitiveArrayToObjectArray(array, Integer.class);
12416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
12426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
12436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
12446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get object array from float array.
12456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
12466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param array Input float array to be converted
12476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return Float object array converted from input float array
12486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
12496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static Float[] toObject(float[] array) {
12506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return convertPrimitiveArrayToObjectArray(array, Float.class);
12516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
12526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
12536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
12546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get object array from double array.
12556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
12566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param array Input double array to be converted
12576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return Double object array converted from input double array
12586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
12596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static Double[] toObject(double[] array) {
12606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return convertPrimitiveArrayToObjectArray(array, Double.class);
12616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
12626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
12636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
12646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Convert a primitive input array into its object array version (e.g. from int[] to Integer[]).
12656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
12666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param array Input array object
12676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param wrapperClass The boxed class it converts to
12686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return Boxed version of primitive array
12696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
12706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static <T> T[] convertPrimitiveArrayToObjectArray(final Object array,
12716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            final Class<T> wrapperClass) {
12726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // getLength does the null check and isArray check already.
12736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int arrayLength = Array.getLength(array);
12746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (arrayLength == 0) {
12756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("Input array shouldn't be empty");
12766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
12776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
12786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        @SuppressWarnings("unchecked")
12796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        final T[] result = (T[]) Array.newInstance(wrapperClass, arrayLength);
12806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        for (int i = 0; i < arrayLength; i++) {
12816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Array.set(result, i, Array.get(array, i));
12826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
12836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return result;
12846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
12856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
12866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
12876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Validate image based on format and size.
12886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
12896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param image The image to be validated.
12906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param width The image width.
12916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param height The image height.
12926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param format The image format.
12936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param filePath The debug dump file path, null if don't want to dump to
12946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *            file.
12956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @throws UnsupportedOperationException if calling with an unknown format
12966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
12976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static void validateImage(Image image, int width, int height, int format,
12986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            String filePath) {
12996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        checkImage(image, width, height, format);
13006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
13016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
13026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * TODO: validate timestamp:
13036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * 1. capture result timestamp against the image timestamp (need
13046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * consider frame drops)
13056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * 2. timestamps should be monotonically increasing for different requests
13066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
13076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if(VERBOSE) Log.v(TAG, "validating Image");
13086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        byte[] data = getDataFromImage(image);
13096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertTrue("Invalid image data", data != null && data.length > 0);
13106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
13116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        switch (format) {
13126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.JPEG:
13136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                validateJpegData(data, width, height, filePath);
13146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                break;
13156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.YUV_420_888:
13166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.YV12:
13176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                validateYuvData(data, width, height, format, image.getTimestamp(), filePath);
13186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                break;
13196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.RAW_SENSOR:
13206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                validateRaw16Data(data, width, height, format, image.getTimestamp(), filePath);
13216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                break;
13226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.DEPTH16:
13236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                validateDepth16Data(data, width, height, format, image.getTimestamp(), filePath);
13246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                break;
13256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.DEPTH_POINT_CLOUD:
13266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                validateDepthPointCloudData(data, width, height, format, image.getTimestamp(), filePath);
13276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                break;
13286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ImageFormat.RAW_PRIVATE:
13296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                validateRawPrivateData(data, width, height, image.getTimestamp(), filePath);
13306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                break;
13316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            default:
13326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                throw new UnsupportedOperationException("Unsupported format for validation: "
13336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        + format);
13346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
13356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
13366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
13376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
13386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Provide a mock for {@link CameraDevice.StateCallback}.
13396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
13406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>Only useful because mockito can't mock {@link CameraDevice.StateCallback} which is an
13416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * abstract class.</p>
13426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
13436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>
13446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Use this instead of other classes when needing to verify interactions, since
13456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * trying to spy on {@link BlockingStateCallback} (or others) will cause unnecessary extra
13466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * interactions which will cause false test failures.
13476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * </p>
13486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
13496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
13506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static class MockStateCallback extends CameraDevice.StateCallback {
13516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
13526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        @Override
13536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void onOpened(CameraDevice camera) {
13546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
13556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
13566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        @Override
13576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void onDisconnected(CameraDevice camera) {
13586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
13596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
13606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        @Override
13616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public void onError(CameraDevice camera, int error) {
13626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
13636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
13646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        private MockStateCallback() {}
13656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
13666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
13676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * Create a Mockito-ready mocked StateCallback.
13686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
13696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public static MockStateCallback mock() {
13706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return Mockito.spy(new MockStateCallback());
13716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
13726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
13736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
13746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static void validateJpegData(byte[] jpegData, int width, int height, String filePath) {
13756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
13766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // DecodeBound mode: only parse the frame header to get width/height.
13776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // it doesn't decode the pixel.
13786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        bmpOptions.inJustDecodeBounds = true;
13796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, bmpOptions);
13806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertEquals(width, bmpOptions.outWidth);
13816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertEquals(height, bmpOptions.outHeight);
13826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
13836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Pixel decoding mode: decode whole image. check if the image data
13846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // is decodable here.
13856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertNotNull("Decoding jpeg failed",
13866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length));
13876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (DEBUG && filePath != null) {
13886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            String fileName =
13896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    filePath + "/" + width + "x" + height + ".jpeg";
13906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            dumpFile(fileName, jpegData);
13916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
13926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
13936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
13946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static void validateYuvData(byte[] yuvData, int width, int height, int format,
13956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            long ts, String filePath) {
13966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        checkYuvFormat(format);
13976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (VERBOSE) Log.v(TAG, "Validating YUV data");
13986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
13996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertEquals("Yuv data doesn't match", expectedSize, yuvData.length);
14006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // TODO: Can add data validation for test pattern.
14026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (DEBUG && filePath != null) {
14046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            String fileName =
14056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".yuv";
14066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            dumpFile(fileName, yuvData);
14076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
14086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
14096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static void validateRaw16Data(byte[] rawData, int width, int height, int format,
14116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            long ts, String filePath) {
14126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (VERBOSE) Log.v(TAG, "Validating raw data");
14136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
14146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertEquals("Raw data doesn't match", expectedSize, rawData.length);
14156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // TODO: Can add data validation for test pattern.
14176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (DEBUG && filePath != null) {
14196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            String fileName =
14206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".raw16";
14216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            dumpFile(fileName, rawData);
14226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
14236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return;
14256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
14266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static void validateRawPrivateData(byte[] rawData, int width, int height,
14286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            long ts, String filePath) {
14296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (VERBOSE) Log.v(TAG, "Validating private raw data");
14306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Expect each RAW pixel should occupy at least one byte and no more than 2.5 bytes
14316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int expectedSizeMin = width * height;
14326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int expectedSizeMax = width * height * 5 / 2;
14336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertTrue("Opaque RAW size " + rawData.length + "out of normal bound [" +
14356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                expectedSizeMin + "," + expectedSizeMax + "]",
14366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                expectedSizeMin <= rawData.length && rawData.length <= expectedSizeMax);
14376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (DEBUG && filePath != null) {
14396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            String fileName =
14406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".rawPriv";
14416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            dumpFile(fileName, rawData);
14426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
14436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return;
14456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
14466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static void validateDepth16Data(byte[] depthData, int width, int height, int format,
14486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            long ts, String filePath) {
14496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (VERBOSE) Log.v(TAG, "Validating depth16 data");
14516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
14526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertEquals("Depth data doesn't match", expectedSize, depthData.length);
14536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (DEBUG && filePath != null) {
14566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            String fileName =
14576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".depth16";
14586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            dumpFile(fileName, depthData);
14596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
14606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return;
14626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
14646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static void validateDepthPointCloudData(byte[] depthData, int width, int height, int format,
14666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            long ts, String filePath) {
14676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (VERBOSE) Log.v(TAG, "Validating depth point cloud data");
14696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Can't validate size since it is variable
14716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (DEBUG && filePath != null) {
14736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            String fileName =
14746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".depth_point_cloud";
14756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            dumpFile(fileName, depthData);
14766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
14776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return;
14796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
14816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static <T> T getValueNotNull(CaptureResult result, CaptureResult.Key<T> key) {
14836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (result == null) {
14846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("Result must not be null");
14856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
14866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        T value = result.get(key);
14886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertNotNull("Value of Key " + key.getName() + "shouldn't be null", value);
14896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return value;
14906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
14916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static <T> T getValueNotNull(CameraCharacteristics characteristics,
14936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraCharacteristics.Key<T> key) {
14946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (characteristics == null) {
14956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("Camera characteristics must not be null");
14966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
14976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
14986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        T value = characteristics.get(key);
14996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertNotNull("Value of Key " + key.getName() + "shouldn't be null", value);
15006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return value;
15016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
15026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
15036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
15046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get a crop region for a given zoom factor and center position.
15056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>
15066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * The center position is normalized position in range of [0, 1.0], where
15076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * (0, 0) represents top left corner, (1.0. 1.0) represents bottom right
15086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * corner. The center position could limit the effective minimal zoom
15096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * factor, for example, if the center position is (0.75, 0.75), the
15106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * effective minimal zoom position becomes 2.0. If the requested zoom factor
15116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * is smaller than 2.0, a crop region with 2.0 zoom factor will be returned.
15126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * </p>
15136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>
15146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * The aspect ratio of the crop region is maintained the same as the aspect
15156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * ratio of active array.
15166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * </p>
15176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
15186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param zoomFactor The zoom factor to generate the crop region, it must be
15196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *            >= 1.0
15206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param center The normalized zoom center point that is in the range of [0, 1].
15216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param maxZoom The max zoom factor supported by this device.
15226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param activeArray The active array size of this device.
15236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return crop region for the given normalized center and zoom factor.
15246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
15256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static Rect getCropRegionForZoom(float zoomFactor, final PointF center,
15266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            final float maxZoom, final Rect activeArray) {
15276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (zoomFactor < 1.0) {
15286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("zoom factor " + zoomFactor + " should be >= 1.0");
15296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
15306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (center.x > 1.0 || center.x < 0) {
15316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("center.x " + center.x
15326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    + " should be in range of [0, 1.0]");
15336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
15346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (center.y > 1.0 || center.y < 0) {
15356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("center.y " + center.y
15366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    + " should be in range of [0, 1.0]");
15376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
15386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (maxZoom < 1.0) {
15396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("max zoom factor " + maxZoom + " should be >= 1.0");
15406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
15416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (activeArray == null) {
15426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("activeArray must not be null");
15436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
15446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
15456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        float minCenterLength = Math.min(Math.min(center.x, 1.0f - center.x),
15466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                Math.min(center.y, 1.0f - center.y));
15476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        float minEffectiveZoom =  0.5f / minCenterLength;
15486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (minEffectiveZoom > maxZoom) {
15496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("Requested center " + center.toString() +
15506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    " has minimal zoomable factor " + minEffectiveZoom + ", which exceeds max"
15516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                            + " zoom factor " + maxZoom);
15526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
15536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
15546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (zoomFactor < minEffectiveZoom) {
15556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Log.w(TAG, "Requested zoomFactor " + zoomFactor + " > minimal zoomable factor "
15566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    + minEffectiveZoom + ". It will be overwritten by " + minEffectiveZoom);
15576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            zoomFactor = minEffectiveZoom;
15586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
15596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
15606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int cropCenterX = (int)(activeArray.width() * center.x);
15616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int cropCenterY = (int)(activeArray.height() * center.y);
15626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int cropWidth = (int) (activeArray.width() / zoomFactor);
15636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int cropHeight = (int) (activeArray.height() / zoomFactor);
15646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
15656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return new Rect(
15666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                /*left*/cropCenterX - cropWidth / 2,
15676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                /*top*/cropCenterY - cropHeight / 2,
15686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                /*right*/ cropCenterX + cropWidth / 2 - 1,
15696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                /*bottom*/cropCenterY + cropHeight / 2 - 1);
15706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
15716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
15726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
15736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Calculate output 3A region from the intersection of input 3A region and cropped region.
15746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
15756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param requestRegions The input 3A regions
15766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param cropRect The cropped region
15776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return expected 3A regions output in capture result
15786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
15796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static MeteringRectangle[] getExpectedOutputRegion(
15806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            MeteringRectangle[] requestRegions, Rect cropRect){
15816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        MeteringRectangle[] resultRegions = new MeteringRectangle[requestRegions.length];
15826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        for (int i = 0; i < requestRegions.length; i++) {
15836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Rect requestRect = requestRegions[i].getRect();
15846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Rect resultRect = new Rect();
15856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            assertTrue("Input 3A region must intersect cropped region",
15866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        resultRect.setIntersect(requestRect, cropRect));
15876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            resultRegions[i] = new MeteringRectangle(
15886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    resultRect,
15896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    requestRegions[i].getMeteringWeight());
15906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
15916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return resultRegions;
15926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
15936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
15946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
15956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Copy source image data to destination image.
15966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
15976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param src The source image to be copied from.
15986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param dst The destination image to be copied to.
15996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @throws IllegalArgumentException If the source and destination images have
16006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *             different format, or one of the images is not copyable.
16016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
16026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static void imageCopy(Image src, Image dst) {
16036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (src == null || dst == null) {
16046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("Images should be non-null");
16056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
16066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (src.getFormat() != dst.getFormat()) {
16076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("Src and dst images should have the same format");
16086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
16096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (src.getFormat() == ImageFormat.PRIVATE ||
16106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                dst.getFormat() == ImageFormat.PRIVATE) {
16116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("PRIVATE format images are not copyable");
16126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
16136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
16146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // TODO: check the owner of the dst image, it must be from ImageWriter, other source may
16156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // not be writable. Maybe we should add an isWritable() method in image class.
16166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
16176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Plane[] srcPlanes = src.getPlanes();
16186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Plane[] dstPlanes = dst.getPlanes();
16196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        ByteBuffer srcBuffer = null;
16206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        ByteBuffer dstBuffer = null;
16216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        for (int i = 0; i < srcPlanes.length; i++) {
16226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            srcBuffer = srcPlanes[i].getBuffer();
16236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            int srcPos = srcBuffer.position();
16246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            srcBuffer.rewind();
16256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            dstBuffer = dstPlanes[i].getBuffer();
16266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            dstBuffer.rewind();
16276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            dstBuffer.put(srcBuffer);
16286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            srcBuffer.position(srcPos);
16296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            dstBuffer.rewind();
16306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
16316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
16326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
16336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
16346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>
16356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Checks whether the two images are strongly equal.
16366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * </p>
16376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>
16386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Two images are strongly equal if and only if the data, formats, sizes,
16396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * and timestamps are same. For {@link ImageFormat#PRIVATE PRIVATE} format
16406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * images, the image data is not not accessible thus the data comparison is
16416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * effectively skipped as the number of planes is zero.
16426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * </p>
16436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>
16446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Note that this method compares the pixel data even outside of the crop
16456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * region, which may not be necessary for general use case.
16466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * </p>
16476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
16486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param lhsImg First image to be compared with.
16496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param rhsImg Second image to be compared with.
16506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return true if the two images are equal, false otherwise.
16516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @throws IllegalArgumentException If either of image is null.
16526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
16536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static boolean isImageStronglyEqual(Image lhsImg, Image rhsImg) {
16546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (lhsImg == null || rhsImg == null) {
16556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throw new IllegalArgumentException("Images should be non-null");
16566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
16576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
16586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (lhsImg.getFormat() != rhsImg.getFormat()) {
16596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Log.i(TAG, "lhsImg format " + lhsImg.getFormat() + " is different with rhsImg format "
16606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    + rhsImg.getFormat());
16616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return false;
16626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
16636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
16646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (lhsImg.getWidth() != rhsImg.getWidth()) {
16656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Log.i(TAG, "lhsImg width " + lhsImg.getWidth() + " is different with rhsImg width "
16666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    + rhsImg.getWidth());
16676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return false;
16686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
16696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
16706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (lhsImg.getHeight() != rhsImg.getHeight()) {
16716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Log.i(TAG, "lhsImg height " + lhsImg.getHeight() + " is different with rhsImg height "
16726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    + rhsImg.getHeight());
16736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return false;
16746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
16756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
16766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (lhsImg.getTimestamp() != rhsImg.getTimestamp()) {
16776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Log.i(TAG, "lhsImg timestamp " + lhsImg.getTimestamp()
16786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    + " is different with rhsImg timestamp " + rhsImg.getTimestamp());
16796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return false;
16806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
16816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
16826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (!lhsImg.getCropRect().equals(rhsImg.getCropRect())) {
16836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Log.i(TAG, "lhsImg crop rect " + lhsImg.getCropRect()
16846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    + " is different with rhsImg crop rect " + rhsImg.getCropRect());
16856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return false;
16866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
16876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
16886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Compare data inside of the image.
16896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Plane[] lhsPlanes = lhsImg.getPlanes();
16906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Plane[] rhsPlanes = rhsImg.getPlanes();
16916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        ByteBuffer lhsBuffer = null;
16926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        ByteBuffer rhsBuffer = null;
16936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        for (int i = 0; i < lhsPlanes.length; i++) {
16946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            lhsBuffer = lhsPlanes[i].getBuffer();
16956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            rhsBuffer = rhsPlanes[i].getBuffer();
16966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (!lhsBuffer.equals(rhsBuffer)) {
16976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                Log.i(TAG, "byte buffers for plane " +  i + " don't matach.");
16986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                return false;
16996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
17006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
17016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
17026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return true;
17036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
17046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
17056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
17066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Set jpeg related keys in a capture request builder.
17076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
17086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param builder The capture request builder to set the keys inl
17096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param exifData The exif data to set.
17106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param thumbnailSize The thumbnail size to set.
17116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param collector The camera error collector to collect errors.
17126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
17136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static void setJpegKeys(CaptureRequest.Builder builder, ExifTestData exifData,
17146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Size thumbnailSize, CameraErrorCollector collector) {
17156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        builder.set(CaptureRequest.JPEG_THUMBNAIL_SIZE, thumbnailSize);
17166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        builder.set(CaptureRequest.JPEG_GPS_LOCATION, exifData.gpsLocation);
17176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        builder.set(CaptureRequest.JPEG_ORIENTATION, exifData.jpegOrientation);
17186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        builder.set(CaptureRequest.JPEG_QUALITY, exifData.jpegQuality);
17196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        builder.set(CaptureRequest.JPEG_THUMBNAIL_QUALITY,
17206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                exifData.thumbnailQuality);
17216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
17226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Validate request set and get.
17236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectEquals("JPEG thumbnail size request set and get should match",
17246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                thumbnailSize, builder.get(CaptureRequest.JPEG_THUMBNAIL_SIZE));
17256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectTrue("GPS locations request set and get should match.",
17266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                areGpsFieldsEqual(exifData.gpsLocation,
17276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                builder.get(CaptureRequest.JPEG_GPS_LOCATION)));
17286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectEquals("JPEG orientation request set and get should match",
17296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                exifData.jpegOrientation,
17306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                builder.get(CaptureRequest.JPEG_ORIENTATION));
17316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectEquals("JPEG quality request set and get should match",
17326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                exifData.jpegQuality, builder.get(CaptureRequest.JPEG_QUALITY));
17336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectEquals("JPEG thumbnail quality request set and get should match",
17346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                exifData.thumbnailQuality,
17356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                builder.get(CaptureRequest.JPEG_THUMBNAIL_QUALITY));
17366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
17376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
17386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
17396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Simple validation of JPEG image size and format.
17406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * <p>
17416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Only validate the image object sanity. It is fast, but doesn't actually
17426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * check the buffer data. Assert is used here as it make no sense to
17436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * continue the test if the jpeg image captured has some serious failures.
17446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * </p>
17456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
17466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param image The captured jpeg image
17476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param expectedSize Expected capture jpeg size
17486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
17496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static void basicValidateJpegImage(Image image, Size expectedSize) {
17506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Size imageSz = new Size(image.getWidth(), image.getHeight());
17516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertTrue(
17526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                String.format("Image size doesn't match (expected %s, actual %s) ",
17536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        expectedSize.toString(), imageSz.toString()), expectedSize.equals(imageSz));
17546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertEquals("Image format should be JPEG", ImageFormat.JPEG, image.getFormat());
17556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertNotNull("Image plane shouldn't be null", image.getPlanes());
17566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        assertEquals("Image plane number should be 1", 1, image.getPlanes().length);
17576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
17586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Jpeg decoding validate was done in ImageReaderTest, no need to duplicate the test here.
17596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
17606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
17616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
17626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Verify the JPEG EXIF and JPEG related keys in a capture result are expected.
17636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * - Capture request get values are same as were set.
17646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * - capture result's exif data is the same as was set by
17656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *   the capture request.
17666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * - new tags in the result set by the camera service are
17676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *   present and semantically correct.
17686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
17696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param image The output JPEG image to verify.
17706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param captureResult The capture result to verify.
17716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param expectedSize The expected JPEG size.
17726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param expectedThumbnailSize The expected thumbnail size.
17736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param expectedExifData The expected EXIF data
17746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param staticInfo The static metadata for the camera device.
17756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param jpegFilename The filename to dump the jpeg to.
17766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param collector The camera error collector to collect errors.
17776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
17786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static void verifyJpegKeys(Image image, CaptureResult captureResult, Size expectedSize,
17796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            Size expectedThumbnailSize, ExifTestData expectedExifData, StaticMetadata staticInfo,
17806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraErrorCollector collector) throws Exception {
17816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
17826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        basicValidateJpegImage(image, expectedSize);
17836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
17846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        byte[] jpegBuffer = getDataFromImage(image);
17856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Have to dump into a file to be able to use ExifInterface
17866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        String jpegFilename = DEBUG_FILE_NAME_BASE + "/verifyJpegKeys.jpeg";
17876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        dumpFile(jpegFilename, jpegBuffer);
17886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        ExifInterface exif = new ExifInterface(jpegFilename);
17896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
17906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (expectedThumbnailSize.equals(new Size(0,0))) {
17916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            collector.expectTrue("Jpeg shouldn't have thumbnail when thumbnail size is (0, 0)",
17926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    !exif.hasThumbnail());
17936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        } else {
17946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            collector.expectTrue("Jpeg must have thumbnail for thumbnail size " +
17956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    expectedThumbnailSize, exif.hasThumbnail());
17966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
17976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
17986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Validate capture result vs. request
17996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Size resultThumbnailSize = captureResult.get(CaptureResult.JPEG_THUMBNAIL_SIZE);
18006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int orientationTested = expectedExifData.jpegOrientation;
18016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Legacy shim always doesn't rotate thumbnail size
18026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if ((orientationTested == 90 || orientationTested == 270) &&
18036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                staticInfo.isHardwareLevelLimitedOrBetter()) {
18046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
18056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    /*defaultValue*/-1);
18066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (exifOrientation == ExifInterface.ORIENTATION_UNDEFINED) {
18076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                // Device physically rotated image+thumbnail data
18086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                // Expect thumbnail size to be also rotated
18096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                resultThumbnailSize = new Size(resultThumbnailSize.getHeight(),
18106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        resultThumbnailSize.getWidth());
18116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
18126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
18136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
18146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectEquals("JPEG thumbnail size result and request should match",
18156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                expectedThumbnailSize, resultThumbnailSize);
18166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (collector.expectKeyValueNotNull(captureResult, CaptureResult.JPEG_GPS_LOCATION) !=
18176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                null) {
18186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            collector.expectTrue("GPS location result and request should match.",
18196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    areGpsFieldsEqual(expectedExifData.gpsLocation,
18206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    captureResult.get(CaptureResult.JPEG_GPS_LOCATION)));
18216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
18226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectEquals("JPEG orientation result and request should match",
18236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                expectedExifData.jpegOrientation,
18246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                captureResult.get(CaptureResult.JPEG_ORIENTATION));
18256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectEquals("JPEG quality result and request should match",
18266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                expectedExifData.jpegQuality, captureResult.get(CaptureResult.JPEG_QUALITY));
18276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectEquals("JPEG thumbnail quality result and request should match",
18286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                expectedExifData.thumbnailQuality,
18296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                captureResult.get(CaptureResult.JPEG_THUMBNAIL_QUALITY));
18306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
18316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Validate other exif tags for all non-legacy devices
18326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (!staticInfo.isHardwareLevelLegacy()) {
18336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            verifyJpegExifExtraTags(exif, expectedSize, captureResult, staticInfo, collector);
18346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
18356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
18366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
18376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
18386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Get the degree of an EXIF orientation.
18396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
18406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static int getExifOrientationInDegree(int exifOrientation,
18416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraErrorCollector collector) {
18426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        switch (exifOrientation) {
18436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ExifInterface.ORIENTATION_NORMAL:
18446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                return 0;
18456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ExifInterface.ORIENTATION_ROTATE_90:
18466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                return 90;
18476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ExifInterface.ORIENTATION_ROTATE_180:
18486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                return 180;
18496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            case ExifInterface.ORIENTATION_ROTATE_270:
18506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                return 270;
18516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            default:
18526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                collector.addMessage("It is impossible to get non 0, 90, 180, 270 degress exif" +
18536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        "info based on the request orientation range");
18546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                return 0;
18556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
18566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
18576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
18586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
18596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Validate and return the focal length.
18606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
18616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param result Capture result to get the focal length
18626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return Focal length from capture result or -1 if focal length is not available.
18636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
18646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static float validateFocalLength(CaptureResult result, StaticMetadata staticInfo,
18656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraErrorCollector collector) {
18666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        float[] focalLengths = staticInfo.getAvailableFocalLengthsChecked();
18676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Float resultFocalLength = result.get(CaptureResult.LENS_FOCAL_LENGTH);
18686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (collector.expectTrue("Focal length is invalid",
18696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                resultFocalLength != null && resultFocalLength > 0)) {
18706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            List<Float> focalLengthList =
18716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    Arrays.asList(CameraTestUtils.toObject(focalLengths));
18726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            collector.expectTrue("Focal length should be one of the available focal length",
18736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    focalLengthList.contains(resultFocalLength));
18746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return resultFocalLength;
18756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
18766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return -1;
18776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
18786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
18796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
18806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Validate and return the aperture.
18816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     *
18826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @param result Capture result to get the aperture
18836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * @return Aperture from capture result or -1 if aperture is not available.
18846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
18856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static float validateAperture(CaptureResult result, StaticMetadata staticInfo,
18866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CameraErrorCollector collector) {
18876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        float[] apertures = staticInfo.getAvailableAperturesChecked();
18886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Float resultAperture = result.get(CaptureResult.LENS_APERTURE);
18896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (collector.expectTrue("Capture result aperture is invalid",
18906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                resultAperture != null && resultAperture > 0)) {
18916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            List<Float> apertureList =
18926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    Arrays.asList(CameraTestUtils.toObject(apertures));
18936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            collector.expectTrue("Aperture should be one of the available apertures",
18946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    apertureList.contains(resultAperture));
18956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return resultAperture;
18966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
18976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return -1;
18986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
18996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
19006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
19016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Return the closest value in an array of floats.
19026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
19036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static float getClosestValueInArray(float[] values, float target) {
19046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int minIdx = 0;
19056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        float minDistance = Math.abs(values[0] - target);
19066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        for(int i = 0; i < values.length; i++) {
19076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            float distance = Math.abs(values[i] - target);
19086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (minDistance > distance) {
19096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                minDistance = distance;
19106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                minIdx = i;
19116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
19126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
19136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
19146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return values[minIdx];
19156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
19166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
19176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
19186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Return if two Location's GPS field are the same.
19196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
19206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static boolean areGpsFieldsEqual(Location a, Location b) {
19216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (a == null || b == null) {
19226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return false;
19236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
19246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
19256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        return a.getTime() == b.getTime() && a.getLatitude() == b.getLatitude() &&
19266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                a.getLongitude() == b.getLongitude() && a.getAltitude() == b.getAltitude() &&
19276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                a.getProvider() == b.getProvider();
19286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
19296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
19306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
19316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Verify extra tags in JPEG EXIF
19326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
19336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    private static void verifyJpegExifExtraTags(ExifInterface exif, Size jpegSize,
19346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            CaptureResult result, StaticMetadata staticInfo, CameraErrorCollector collector)
19356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            throws ParseException {
19366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
19376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * TAG_IMAGE_WIDTH and TAG_IMAGE_LENGTH and TAG_ORIENTATION.
19386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * Orientation and exif width/height need to be tested carefully, two cases:
19396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
19406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * 1. Device rotate the image buffer physically, then exif width/height may not match
19416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * the requested still capture size, we need swap them to check.
19426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
19436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * 2. Device use the exif tag to record the image orientation, it doesn't rotate
19446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * the jpeg image buffer itself. In this case, the exif width/height should always match
19456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * the requested still capture size, and the exif orientation should always match the
19466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * requested orientation.
19476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         *
19486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
19496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int exifWidth = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, /*defaultValue*/0);
19506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int exifHeight = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, /*defaultValue*/0);
19516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Size exifSize = new Size(exifWidth, exifHeight);
19526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Orientation could be missing, which is ok, default to 0.
19536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
19546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                /*defaultValue*/-1);
19556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // Get requested orientation from result, because they should be same.
19566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (collector.expectKeyValueNotNull(result, CaptureResult.JPEG_ORIENTATION) != null) {
19576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            int requestedOrientation = result.get(CaptureResult.JPEG_ORIENTATION);
19586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            final int ORIENTATION_MIN = ExifInterface.ORIENTATION_UNDEFINED;
19596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            final int ORIENTATION_MAX = ExifInterface.ORIENTATION_ROTATE_270;
19606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            boolean orientationValid = collector.expectTrue(String.format(
19616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    "Exif orientation must be in range of [%d, %d]",
19626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    ORIENTATION_MIN, ORIENTATION_MAX),
19636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    exifOrientation >= ORIENTATION_MIN && exifOrientation <= ORIENTATION_MAX);
19646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (orientationValid) {
19656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                /**
19666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                 * Device captured image doesn't respect the requested orientation,
19676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                 * which means it rotates the image buffer physically. Then we
19686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                 * should swap the exif width/height accordingly to compare.
19696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                 */
19706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                boolean deviceRotatedImage = exifOrientation == ExifInterface.ORIENTATION_UNDEFINED;
19716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
19726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                if (deviceRotatedImage) {
19736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    // Case 1.
19746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    boolean needSwap = (requestedOrientation % 180 == 90);
19756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    if (needSwap) {
19766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        exifSize = new Size(exifHeight, exifWidth);
19776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    }
19786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                } else {
19796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    // Case 2.
19806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    collector.expectEquals("Exif orientaiton should match requested orientation",
19816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                            requestedOrientation, getExifOrientationInDegree(exifOrientation,
19826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                            collector));
19836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                }
19846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
19856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
19866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
19876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
19886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * Ideally, need check exifSize == jpegSize == actual buffer size. But
19896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * jpegSize == jpeg decode bounds size(from jpeg jpeg frame
19906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * header, not exif) was validated in ImageReaderTest, no need to
19916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * validate again here.
19926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
19936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectEquals("Exif size should match jpeg capture size", jpegSize, exifSize);
19946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
19956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // TAG_DATETIME, it should be local time
19966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        long currentTimeInMs = System.currentTimeMillis();
19976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        long currentTimeInSecond = currentTimeInMs / 1000;
19986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Date date = new Date(currentTimeInMs);
19996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        String localDatetime = new SimpleDateFormat("yyyy:MM:dd HH:").format(date);
20006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        String dateTime = exif.getAttribute(ExifInterface.TAG_DATETIME);
20016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (collector.expectTrue("Exif TAG_DATETIME shouldn't be null", dateTime != null)) {
20026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            collector.expectTrue("Exif TAG_DATETIME is wrong",
20036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    dateTime.length() == EXIF_DATETIME_LENGTH);
20046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            long exifTimeInSecond =
20056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    new SimpleDateFormat("yyyy:MM:dd HH:mm:ss").parse(dateTime).getTime() / 1000;
20066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            long delta = currentTimeInSecond - exifTimeInSecond;
20076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            collector.expectTrue("Capture time deviates too much from the current time",
20086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    Math.abs(delta) < EXIF_DATETIME_ERROR_MARGIN_SEC);
20096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            // It should be local time.
20106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            collector.expectTrue("Exif date time should be local time",
20116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                    dateTime.startsWith(localDatetime));
20126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
20136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
20146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // TAG_FOCAL_LENGTH.
20156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        float[] focalLengths = staticInfo.getAvailableFocalLengthsChecked();
20166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        float exifFocalLength = (float)exif.getAttributeDouble(ExifInterface.TAG_FOCAL_LENGTH, -1);
20176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectEquals("Focal length should match",
20186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                getClosestValueInArray(focalLengths, exifFocalLength),
20196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                exifFocalLength, EXIF_FOCAL_LENGTH_ERROR_MARGIN);
20206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // More checks for focal length.
20216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectEquals("Exif focal length should match capture result",
20226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                validateFocalLength(result, staticInfo, collector), exifFocalLength);
20236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
20246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // TAG_EXPOSURE_TIME
20256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // ExifInterface API gives exposure time value in the form of float instead of rational
20266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        String exposureTime = exif.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
20276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectNotNull("Exif TAG_EXPOSURE_TIME shouldn't be null", exposureTime);
20286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (staticInfo.areKeysAvailable(CaptureResult.SENSOR_EXPOSURE_TIME)) {
20296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (exposureTime != null) {
20306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                double exposureTimeValue = Double.parseDouble(exposureTime);
20316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                long expTimeResult = result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
20326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                double expected = expTimeResult / 1e9;
20336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                double tolerance = expected * EXIF_EXPOSURE_TIME_ERROR_MARGIN_RATIO;
20346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                tolerance = Math.max(tolerance, EXIF_EXPOSURE_TIME_MIN_ERROR_MARGIN_SEC);
20356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                collector.expectEquals("Exif exposure time doesn't match", expected,
20366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        exposureTimeValue, tolerance);
20376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
20386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
20396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
20406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // TAG_APERTURE
20416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // ExifInterface API gives aperture value in the form of float instead of rational
20426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        String exifAperture = exif.getAttribute(ExifInterface.TAG_APERTURE);
20436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectNotNull("Exif TAG_APERTURE shouldn't be null", exifAperture);
20446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (staticInfo.areKeysAvailable(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES)) {
20456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            float[] apertures = staticInfo.getAvailableAperturesChecked();
20466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (exifAperture != null) {
20476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                float apertureValue = Float.parseFloat(exifAperture);
20486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                collector.expectEquals("Aperture value should match",
20496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        getClosestValueInArray(apertures, apertureValue),
20506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        apertureValue, EXIF_APERTURE_ERROR_MARGIN);
20516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                // More checks for aperture.
20526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                collector.expectEquals("Exif aperture length should match capture result",
20536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        validateAperture(result, staticInfo, collector), apertureValue);
20546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
20556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
20566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
20576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
20586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * TAG_FLASH. TODO: For full devices, can check a lot more info
20596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * (http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html#Flash)
20606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
20616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        String flash = exif.getAttribute(ExifInterface.TAG_FLASH);
20626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectNotNull("Exif TAG_FLASH shouldn't be null", flash);
20636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
20646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
20656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * TAG_WHITE_BALANCE. TODO: For full devices, with the DNG tags, we
20666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * should be able to cross-check android.sensor.referenceIlluminant.
20676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
20686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        String whiteBalance = exif.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
20696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectNotNull("Exif TAG_WHITE_BALANCE shouldn't be null", whiteBalance);
20706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
20716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // TAG_MAKE
20726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        String make = exif.getAttribute(ExifInterface.TAG_MAKE);
20736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectEquals("Exif TAG_MAKE is incorrect", Build.MANUFACTURER, make);
20746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
20756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // TAG_MODEL
20766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        String model = exif.getAttribute(ExifInterface.TAG_MODEL);
20776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectEquals("Exif TAG_MODEL is incorrect", Build.MODEL, model);
20786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
20796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
20806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // TAG_ISO
20816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int iso = exif.getAttributeInt(ExifInterface.TAG_ISO, /*defaultValue*/-1);
20826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (staticInfo.areKeysAvailable(CaptureResult.SENSOR_SENSITIVITY)) {
20836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            int expectedIso = result.get(CaptureResult.SENSOR_SENSITIVITY);
20846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            collector.expectEquals("Exif TAG_ISO is incorrect", expectedIso, iso);
20856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
20866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
20876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // TAG_DATETIME_DIGITIZED (a.k.a Create time for digital cameras).
20886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        String digitizedTime = exif.getAttribute(ExifInterface.TAG_DATETIME_DIGITIZED);
20896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectNotNull("Exif TAG_DATETIME_DIGITIZED shouldn't be null", digitizedTime);
20906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (digitizedTime != null) {
20916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            String expectedDateTime = exif.getAttribute(ExifInterface.TAG_DATETIME);
20926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            collector.expectNotNull("Exif TAG_DATETIME shouldn't be null", expectedDateTime);
20936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            if (expectedDateTime != null) {
20946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                collector.expectEquals("dataTime should match digitizedTime",
20956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                        expectedDateTime, digitizedTime);
20966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            }
20976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
20986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
20996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        /**
21006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * TAG_SUBSEC_TIME. Since the sub second tag strings are truncated to at
21016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * most 9 digits in ExifInterface implementation, use getAttributeInt to
21026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * sanitize it. When the default value -1 is returned, it means that
21036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * this exif tag either doesn't exist or is a non-numerical invalid
21046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         * string. Same rule applies to the rest of sub second tags.
21056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim         */
21066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int subSecTime = exif.getAttributeInt(ExifInterface.TAG_SUBSEC_TIME, /*defaultValue*/-1);
21076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectTrue("Exif TAG_SUBSEC_TIME value is null or invalid!", subSecTime > 0);
21086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
21096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // TAG_SUBSEC_TIME_ORIG
21106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int subSecTimeOrig = exif.getAttributeInt(ExifInterface.TAG_SUBSEC_TIME_ORIG,
21116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                /*defaultValue*/-1);
21126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectTrue("Exif TAG_SUBSEC_TIME_ORIG value is null or invalid!",
21136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                subSecTimeOrig > 0);
21146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
21156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        // TAG_SUBSEC_TIME_DIG
21166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int subSecTimeDig = exif.getAttributeInt(ExifInterface.TAG_SUBSEC_TIME_DIG,
21176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                /*defaultValue*/-1);
21186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        collector.expectTrue(
21196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                "Exif TAG_SUBSEC_TIME_DIG value is null or invalid!", subSecTimeDig > 0);
21206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
21216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
21226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
21236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    /**
21246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     * Immutable class wrapping the exif test data.
21256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim     */
21266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static class ExifTestData {
21276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public final Location gpsLocation;
21286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public final int jpegOrientation;
21296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public final byte jpegQuality;
21306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public final byte thumbnailQuality;
21316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
21326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        public ExifTestData(Location location, int orientation,
21336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim                byte jpgQuality, byte thumbQuality) {
21346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            gpsLocation = location;
21356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            jpegOrientation = orientation;
21366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            jpegQuality = jpgQuality;
21376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            thumbnailQuality = thumbQuality;
21386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
21396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
21406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
21416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    public static Size getPreviewSizeBound(WindowManager windowManager, Size bound) {
21426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        Display display = windowManager.getDefaultDisplay();
21436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
21446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int width = display.getWidth();
21456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        int height = display.getHeight();
21466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
21476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (height > width) {
21486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            height = width;
21496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            width = display.getHeight();
21506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        }
21516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim
21526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        if (bound.getWidth() <= width &&
21536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            bound.getHeight() <= height)
21546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return bound;
21556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim        else
21566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim            return new Size(width, height);
21576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim    }
21586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim}
2159