merge in nyc-release history after reset to nyc-dev
diff --git a/tests/tests/media/assets/520x360h264decodertest.mp4 b/tests/tests/media/assets/520x360h264decodertest.mp4
new file mode 100644
index 0000000..362568c
--- /dev/null
+++ b/tests/tests/media/assets/520x360h264decodertest.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/h264decodertest520x360golden.png b/tests/tests/media/res/raw/h264decodertest520x360golden.png
new file mode 100644
index 0000000..fe4663d
--- /dev/null
+++ b/tests/tests/media/res/raw/h264decodertest520x360golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/h264decodertest520x360original.png b/tests/tests/media/res/raw/h264decodertest520x360original.png
new file mode 100644
index 0000000..c0fd752
--- /dev/null
+++ b/tests/tests/media/res/raw/h264decodertest520x360original.png
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java b/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java
index 3f4dde0..0a3165b 100644
--- a/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java
+++ b/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java
@@ -19,98 +19,123 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.cts.util.MediaUtils;
 import android.graphics.Bitmap;
-import android.os.Build;
-import android.util.DisplayMetrics;
-import android.util.Log;
 import android.view.View;
-import android.view.WindowManager;
 
-@TargetApi(16)
+@TargetApi(24)
 public class DecodeAccuracyTest extends DecodeAccuracyTestBase {
 
     private static final String TAG = DecodeAccuracyTest.class.getSimpleName();
     private static final String H264_VIDEO_FILE_NAME = "480ph264decodertest.mp4";
     private static final String VP9_VIDEO_FILE_NAME = "360pvp9decodertest.webm";
+    private static final String H264_CROPPED_VIDEO_FILE_NAME = "520x360h264decodertest.mp4";
     private static final int ALLOWED_GREATEST_PIXEL_DIFFERENCE = 90;
     private static final int OFFSET = 10;
 
     /* <------------- Tests Using H264 -------------> */
     public void testH264GLViewVideoDecode() throws Exception {
-        runDecodeAccuracyTest(
+        runH264DecodeAccuracyTest(
                 new GLSurfaceViewFactory(),
                 new VideoFormat(H264_VIDEO_FILE_NAME));
     }
 
     public void testH264GLViewLargerHeightVideoDecode() throws Exception {
-        runDecodeAccuracyTest(
+        runH264DecodeAccuracyTest(
                 new GLSurfaceViewFactory(),
                 getLargerHeightVideoFormat(new VideoFormat(H264_VIDEO_FILE_NAME)));
     }
 
     public void testH264GLViewLargerWidthVideoDecode() throws Exception {
-        runDecodeAccuracyTest(
+        runH264DecodeAccuracyTest(
                 new GLSurfaceViewFactory(),
                 getLargerWidthVideoFormat(new VideoFormat(H264_VIDEO_FILE_NAME)));
     }
 
     public void testH264SurfaceViewVideoDecode() throws Exception {
-        runDecodeAccuracyTest(
+        runH264DecodeAccuracyTest(
                 new SurfaceViewFactory(),
                 new VideoFormat(H264_VIDEO_FILE_NAME));
     }
 
     public void testH264SurfaceViewLargerHeightVideoDecode() throws Exception {
-        runDecodeAccuracyTest(
+        runH264DecodeAccuracyTest(
                 new SurfaceViewFactory(),
                 getLargerHeightVideoFormat(new VideoFormat(H264_VIDEO_FILE_NAME)));
     }
 
     public void testH264SurfaceViewLargerWidthVideoDecode() throws Exception {
-        runDecodeAccuracyTest(
+        runH264DecodeAccuracyTest(
                 new SurfaceViewFactory(),
                 getLargerWidthVideoFormat(new VideoFormat(H264_VIDEO_FILE_NAME)));
     }
 
     /* <------------- Tests Using VP9 -------------> */
     public void testVP9GLViewVideoDecode() throws Exception {
-        runDecodeAccuracyTest(
+        runVP9DecodeAccuracyTest(
                 new GLSurfaceViewFactory(),
                 new VideoFormat(VP9_VIDEO_FILE_NAME));
     }
 
     public void testVP9GLViewLargerHeightVideoDecode() throws Exception {
-        runDecodeAccuracyTest(
+        runVP9DecodeAccuracyTest(
                 new GLSurfaceViewFactory(),
                 getLargerHeightVideoFormat(new VideoFormat(VP9_VIDEO_FILE_NAME)));
     }
 
     public void testVP9GLViewLargerWidthVideoDecode() throws Exception {
-        runDecodeAccuracyTest(
+        runVP9DecodeAccuracyTest(
                 new GLSurfaceViewFactory(),
                 getLargerWidthVideoFormat(new VideoFormat(VP9_VIDEO_FILE_NAME)));
     }
 
     public void testVP9SurfaceViewVideoDecode() throws Exception {
-        runDecodeAccuracyTest(
+        runVP9DecodeAccuracyTest(
                 new SurfaceViewFactory(),
                 new VideoFormat(VP9_VIDEO_FILE_NAME));
     }
 
     public void testVP9SurfaceViewLargerHeightVideoDecode() throws Exception {
-        runDecodeAccuracyTest(
+        runVP9DecodeAccuracyTest(
                 new SurfaceViewFactory(),
                 getLargerHeightVideoFormat(new VideoFormat(VP9_VIDEO_FILE_NAME)));
     }
 
     public void testVP9SurfaceViewLargerWidthVideoDecode() throws Exception {
-        runDecodeAccuracyTest(
+        runVP9DecodeAccuracyTest(
                 new SurfaceViewFactory(),
                 getLargerWidthVideoFormat(new VideoFormat(VP9_VIDEO_FILE_NAME)));
     }
 
-    private void runDecodeAccuracyTest(VideoViewFactory videoViewFactory, VideoFormat videoFormat) {
+    /* <------------- Tests H264 with cropping -------------> */
+    public void testH264GLViewCroppedVideoDecode() throws Exception {
+        runH264DecodeCroppedTest(
+                new GLSurfaceViewFactory(),
+                new VideoFormat(H264_CROPPED_VIDEO_FILE_NAME));
+    }
+
+    public void testH264SurfaceViewCroppedVideoDecode() throws Exception {
+        runH264DecodeCroppedTest(
+                new SurfaceViewFactory(),
+                new VideoFormat(H264_CROPPED_VIDEO_FILE_NAME));
+    }
+
+    private void runH264DecodeAccuracyTest(
+            VideoViewFactory videoViewFactory, VideoFormat videoFormat) {
+        runDecodeAccuracyTest(videoViewFactory, videoFormat, R.raw.h264decodertestgolden);
+    }
+
+    private void runVP9DecodeAccuracyTest(
+            VideoViewFactory videoViewFactory, VideoFormat videoFormat) {
+        runDecodeAccuracyTest(videoViewFactory, videoFormat, R.raw.vp9decodertestgolden);
+    }
+
+    private void runH264DecodeCroppedTest(
+            VideoViewFactory videoViewFactory, VideoFormat videoFormat) {
+        runDecodeAccuracyTest(videoViewFactory, videoFormat, R.raw.h264decodertest520x360golden);
+    }
+
+    private void runDecodeAccuracyTest(
+            VideoViewFactory videoViewFactory, VideoFormat videoFormat, int goldenResId) {
         checkNotNull(videoViewFactory);
         checkNotNull(videoFormat);
         View videoView = videoViewFactory.createView(getHelper().getContext());
@@ -123,7 +148,7 @@
         // so it needs to be created before start decoding.
         final VideoViewSnapshot videoViewSnapshot = videoViewFactory.getVideoViewSnapshot();
         decodeVideo(videoFormat, videoViewFactory);
-        validateResult(videoFormat, videoViewSnapshot);
+        validateResult(videoFormat, videoViewSnapshot, goldenResId);
 
         if (videoView != null) {
             getHelper().cleanUpView(videoView);
@@ -140,20 +165,16 @@
         assertTrue("Failed to decode the video.", playerResult.isSuccess());
     }
 
-    private void validateResult(VideoFormat videoFormat, VideoViewSnapshot videoViewSnapshot) {
+    private void validateResult(
+            VideoFormat videoFormat, VideoViewSnapshot videoViewSnapshot, int goldenResId) {
         final Bitmap result = getHelper().generateBitmapFromVideoViewSnapshot(videoViewSnapshot);
-        final Bitmap golden;
-        final String mime = videoFormat.getMimeType();
-        if (mime.equals(MimeTypes.VIDEO_H264)) {
-            golden = getHelper().generateBitmapFromImageResourceId(R.raw.h264decodertestgolden);
-        } else if (mime.equals(MimeTypes.VIDEO_VP9)) {
-            golden = getHelper().generateBitmapFromImageResourceId(R.raw.vp9decodertestgolden);
-        } else {
-            fail("Unsupported MIME type " + mime);
-            return;
-        }
-        final BitmapCompare.Difference difference = BitmapCompare.computeDifference(golden, result);
-        assertTrue("Greatest pixel difference is "
+        final Bitmap golden = getHelper().generateBitmapFromImageResourceId(goldenResId);
+        final BitmapCompare.Difference difference = BitmapCompare.computeMinimumDifference(
+                result, golden, videoFormat.getOriginalWidth(), videoFormat.getOriginalHeight());
+        assertTrue("With the best matched border crop ("
+                    + difference.bestMatchBorderCrop.first + ", "
+                    + difference.bestMatchBorderCrop.second + "), "
+                    + "greatest pixel difference is "
                     + difference.greatestPixelDifference
                     + (difference.greatestPixelDifferenceCoordinates != null
                     ? " at (" + difference.greatestPixelDifferenceCoordinates.first + ", "
diff --git a/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java b/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java
index 3e3c866f..47029d6b 100644
--- a/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java
+++ b/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java
@@ -617,6 +617,7 @@
 /**
  * Factory for building a {@link SurfaceView}
  */
+@TargetApi(24)
 class SurfaceViewFactory extends VideoViewFactory implements SurfaceHolder.Callback {
 
     private static final String TAG = SurfaceViewFactory.class.getSimpleName();
@@ -1294,6 +1295,8 @@
     private int height = UNSET;
     private int maxWidth = UNSET;
     private int maxHeight = UNSET;
+    private int originalWidth = UNSET;
+    private int originalHeight = UNSET;
 
     public VideoFormat(String filename, Uri uri) {
         this.filename = filename;
@@ -1331,6 +1334,9 @@
 
     public void setWidth(int width) {
         this.width = width;
+        if (this.originalWidth == UNSET) {
+            this.originalWidth = width;
+        }
     }
 
     public void setMaxWidth(int maxWidth) {
@@ -1345,8 +1351,15 @@
         return maxWidth;
     }
 
+    public int getOriginalWidth() {
+        return originalWidth;
+    }
+
     public void setHeight(int height) {
         this.height = height;
+        if (this.originalHeight == UNSET) {
+            this.originalHeight = height;
+        }
     }
 
     public void setMaxHeight(int maxHeight) {
@@ -1361,6 +1374,10 @@
         return maxHeight;
     }
 
+    public int getOriginalHeight() {
+        return originalHeight;
+    }
+
     private Uri createCacheFile(Context context) {
         try {
             File cacheFile = new File(context.getCacheDir(), filename);
@@ -1563,22 +1580,167 @@
         return (int) Math.round(Math.sqrt(result));
     }
 
+    /**
+     * Crops the border of the array representing an image by hBorderSize
+     * pixels on the left and right borders, and by vBorderSize pixels on the
+     * top and bottom borders (so the width is 2 * hBorderSize smaller and
+     * the height is 2 * vBorderSize smaller), then scales the image up to
+     * match the original size using bilinear interpolation.
+     */
+    private static Bitmap shrinkAndScaleBilinear(
+            Bitmap input, double hBorderSize, double vBorderSize) {
+
+        int width = input.getWidth();
+        int height = input.getHeight();
+
+        // Compute the proper step sizes
+        double xInc = ((double) width - 1 - hBorderSize * 2) / (double) (width - 1);
+        double yInc = ((double) height - 1 - vBorderSize * 2) / (double) (height - 1);
+
+        // Read the input bitmap into RGB arrays.
+        int[] inputPixels = new int[width * height];
+        input.getPixels(inputPixels, 0, width, 0, 0, width, height);
+        int[][] inputRgb = new int[width * height][3];
+        for (int i = 0; i < width * height; ++i) {
+            inputRgb[i][0] = Color.red(inputPixels[i]);
+            inputRgb[i][1] = Color.green(inputPixels[i]);
+            inputRgb[i][2] = Color.blue(inputPixels[i]);
+        }
+        inputPixels = null;
+
+        // Prepare the output buffer.
+        int[] outputPixels = new int[width * height];
+
+        // Start the iteration. The first y coordinate is vBorderSize.
+        double y = vBorderSize;
+        for (int yIndex = 0; yIndex < height; ++yIndex) {
+            // The first x coordinate is hBorderSize.
+            double x = hBorderSize;
+            for (int xIndex = 0; xIndex < width; ++xIndex) {
+                // Determine the square of interest.
+                int left = (int)x;    // This is floor(x).
+                int top = (int)y;     // This is floor(y).
+                int right = left + 1;
+                int bottom = top + 1;
+
+                // (u, v) is the fractional part of (x, y).
+                double u = x - (double)left;
+                double v = y - (double)top;
+
+                // Precompute necessary products to save time.
+                double p00 = (1.0 - u) * (1.0 - v);
+                double p01 = (1.0 - u) * v;
+                double p10 = u * (1.0 - v);
+                double p11 = u * v;
+
+                // Clamp the indices to prevent out-of-bound that may be caused
+                // by round-off error.
+                if (left >= width) left = width - 1;
+                if (top >= height) top = height - 1;
+                if (right >= width) right = width - 1;
+                if (bottom >= height) bottom = height - 1;
+
+                // Sample RGB values from the four corners.
+                int[] rgb00 = inputRgb[top * width + left];
+                int[] rgb01 = inputRgb[bottom * width + left];
+                int[] rgb10 = inputRgb[top * width + right];
+                int[] rgb11 = inputRgb[bottom * width + right];
+
+                // Interpolate each component of RGB separately.
+                int[] mixedColor = new int[3];
+                for (int k = 0; k < 3; ++k) {
+                    mixedColor[k] = (int)Math.round(
+                            p00 * (double) rgb00[k] + p01 * (double) rgb01[k]
+                            + p10 * (double) rgb10[k] + p11 * (double) rgb11[k]);
+                }
+                // Convert RGB to bitmap Color format and store.
+                outputPixels[yIndex * width + xIndex] = Color.rgb(
+                        mixedColor[0], mixedColor[1], mixedColor[2]);
+                x += xInc;
+            }
+            y += yInc;
+        }
+        // Assemble the output buffer into a Bitmap object.
+        return Bitmap.createBitmap(outputPixels, width, height, input.getConfig());
+    }
+
+    /**
+     * Calls computeDifference on multiple cropped-and-scaled versions of
+     * bitmap2.
+     */
+    @TargetApi(12)
+    public static Difference computeMinimumDifference(
+            Bitmap bitmap1, Bitmap bitmap2, Pair<Double, Double>[] borderCrops) {
+
+        // Compute the difference with the original image (bitmap2) first.
+        Difference minDiff = computeDifference(bitmap1, bitmap2);
+        // Then go through the list of borderCrops.
+        for (Pair<Double, Double> borderCrop : borderCrops) {
+            // Compute the difference between bitmap1 and a transformed
+            // version of bitmap2.
+            Bitmap bitmap2s = shrinkAndScaleBilinear(bitmap2, borderCrop.first, borderCrop.second);
+            Difference d = computeDifference(bitmap1, bitmap2s);
+            // Keep the minimum difference.
+            if (d.greatestPixelDifference < minDiff.greatestPixelDifference) {
+                minDiff = d;
+                minDiff.bestMatchBorderCrop = borderCrop;
+            }
+        }
+        return minDiff;
+    }
+
+    /**
+     * Calls computeMinimumDifference on a default list of borderCrop.
+     */
+    @TargetApi(12)
+    public static Difference computeMinimumDifference(
+            Bitmap bitmap1, Bitmap bitmap2, int trueWidth, int trueHeight) {
+
+        double hBorder = (double) bitmap1.getWidth() / (double) trueWidth;
+        double vBorder = (double) bitmap1.getHeight() / (double) trueHeight;
+        double hBorderH = 0.5 * hBorder; // Half-texel horizontal border
+        double vBorderH = 0.5 * vBorder; // Half-texel vertical border
+        return computeMinimumDifference(
+                bitmap1,
+                bitmap2,
+                new Pair[] {
+                    Pair.create(hBorderH, 0.0),
+                    Pair.create(hBorderH, vBorderH),
+                    Pair.create(0.0, vBorderH),
+                    Pair.create(hBorder, 0.0),
+                    Pair.create(hBorder, vBorder),
+                    Pair.create(0.0, vBorder)
+                });
+        // This default list of borderCrop comes from the behavior of
+        // GLConsumer.computeTransformMatrix().
+    }
+
     /* Describes the difference between two {@link Bitmap} instances. */
     public static final class Difference {
 
         public final int greatestPixelDifference;
         public final Pair<Integer, Integer> greatestPixelDifferenceCoordinates;
+        public Pair<Double, Double> bestMatchBorderCrop;
 
         private Difference(int greatestPixelDifference) {
-            this(greatestPixelDifference, null);
+            this(greatestPixelDifference, null, Pair.create(0.0, 0.0));
         }
 
         private Difference(
                 int greatestPixelDifference,
                 Pair<Integer, Integer> greatestPixelDifferenceCoordinates) {
+            this(greatestPixelDifference, greatestPixelDifferenceCoordinates,
+                    Pair.create(0.0, 0.0));
+        }
+
+        private Difference(
+                int greatestPixelDifference,
+                Pair<Integer, Integer> greatestPixelDifferenceCoordinates,
+                Pair<Double, Double> bestMatchBorderCrop) {
             this.greatestPixelDifference = greatestPixelDifference;
             this.greatestPixelDifferenceCoordinates = greatestPixelDifferenceCoordinates;
-        }
+            this.bestMatchBorderCrop = bestMatchBorderCrop;
+       }
     }
 
 }