camera2: Add re-usable data types for camera metadata key/values

Adds new types to public API:
* ColorSpaceTransform
* Range<T>
* SizeF (android.util)
* MeteringRectangle

Minor changes to:
* Size (docs and move to android.util)
* Preconditions (@hide)

Adds helper class:
* HashCodeHelpers

Change-Id: Ied6749a19234f3af9da838f365f8d68d90251aaf
diff --git a/api/current.txt b/api/current.txt
index 9dff2f9..be5ad9b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12217,6 +12217,14 @@
     field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MODE;
   }
 
+  public final class ColorSpaceTransform {
+    ctor public ColorSpaceTransform(android.hardware.camera2.Rational[]);
+    ctor public ColorSpaceTransform(int[]);
+    method public void copyElements(android.hardware.camera2.Rational[], int);
+    method public void copyElements(int[], int);
+    method public android.hardware.camera2.Rational getElement(int, int);
+  }
+
   public final class Face {
     method public android.graphics.Rect getBounds();
     method public int getId();
@@ -12229,6 +12237,21 @@
     field public static final int SCORE_MIN = 1; // 0x1
   }
 
+  public final class MeteringRectangle {
+    ctor public MeteringRectangle(int, int, int, int, int);
+    ctor public MeteringRectangle(android.graphics.Point, android.util.Size, int);
+    ctor public MeteringRectangle(android.graphics.Rect, int);
+    method public boolean equals(android.hardware.camera2.MeteringRectangle);
+    method public int getHeight();
+    method public int getMeteringWeight();
+    method public android.graphics.Rect getRect();
+    method public android.util.Size getSize();
+    method public android.graphics.Point getUpperLeftPoint();
+    method public int getWidth();
+    method public int getX();
+    method public int getY();
+  }
+
   public final class Rational {
     ctor public Rational(int, int);
     method public int getDenominator();
@@ -28709,6 +28732,25 @@
     method public void set(T, V);
   }
 
+  public final class Range {
+    ctor public Range(T, T);
+    method public static android.util.Range<T> create(T, T);
+    method public T getLower();
+    method public T getUpper();
+  }
+
+  public final class Size {
+    ctor public Size(int, int);
+    method public int getHeight();
+    method public int getWidth();
+  }
+
+  public final class SizeF {
+    ctor public SizeF(float, float);
+    method public float getHeight();
+    method public float getWidth();
+  }
+
   public class SparseArray implements java.lang.Cloneable {
     ctor public SparseArray();
     ctor public SparseArray(int);
diff --git a/core/java/android/hardware/camera2/ColorSpaceTransform.java b/core/java/android/hardware/camera2/ColorSpaceTransform.java
new file mode 100644
index 0000000..9912e4b
--- /dev/null
+++ b/core/java/android/hardware/camera2/ColorSpaceTransform.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2;
+
+import static com.android.internal.util.Preconditions.*;
+import android.hardware.camera2.impl.HashCodeHelpers;
+
+import java.util.Arrays;
+
+/**
+ * Immutable class for describing a 3x3 matrix of {@link Rational} values in row-major order.
+ *
+ * <p>This matrix maps a transform from one color space to another. For the particular color space
+ * source and target, see the appropriate camera metadata documentation for the key that provides
+ * this value.</p>
+ *
+ * @see CameraMetadata
+ */
+public final class ColorSpaceTransform {
+
+    /** The number of rows in this matrix. */
+    private static final int ROWS = 3;
+
+    /** The number of columns in this matrix. */
+    private static final int COLUMNS = 3;
+
+    /** The number of total Rational elements in this matrix. */
+    private static final int COUNT = ROWS * COLUMNS;
+
+    /** Number of int elements in a rational. */
+    private static final int RATIONAL_SIZE = 2;
+
+    /** Numerator offset inside a rational (pair). */
+    private static final int OFFSET_NUMERATOR = 0;
+
+    /** Denominator offset inside a rational (pair). */
+    private static final int OFFSET_DENOMINATOR = 1;
+
+    /** Number of int elements in this matrix. */
+    private static final int COUNT_INT = ROWS * COLUMNS * RATIONAL_SIZE;
+
+    /**
+     * Create a new immutable {@link ColorSpaceTransform} instance from a {@link Rational} array.
+     *
+     * <p>The elements must be stored in a row-major order.</p>
+     *
+     * @param elements An array of {@code 9} elements
+     *
+     * @throws IllegalArgumentException
+     *            if the count of {@code elements} is not {@code 9}
+     * @throws NullPointerException
+     *            if {@code elements} or any sub-element is {@code null}
+     */
+    public ColorSpaceTransform(Rational[] elements) {
+
+        checkNotNull(elements, "elements must not be null");
+        if (elements.length != COUNT) {
+            throw new IllegalArgumentException("elements must be " + COUNT + " length");
+        }
+
+        mElements = new int[COUNT_INT];
+
+        for (int i = 0; i < elements.length; ++i) {
+            checkNotNull(elements, "element[" + i + "] must not be null");
+            mElements[i * RATIONAL_SIZE + OFFSET_NUMERATOR] = elements[i].getNumerator();
+            mElements[i * RATIONAL_SIZE + OFFSET_DENOMINATOR] = elements[i].getDenominator();
+        }
+    }
+
+    /**
+     * Create a new immutable {@link ColorSpaceTransform} instance from an {@code int} array.
+     *
+     * <p>The elements must be stored in a row-major order. Each rational is stored
+     * contiguously as a {@code (numerator, denominator)} pair.</p>
+     *
+     * <p>In particular:<pre>{@code
+     * int[] elements = new int[
+     *     N11, D11, N12, D12, N13, D13,
+     *     N21, D21, N22, D22, N23, D23,
+     *     N31, D31, N32, D32, N33, D33
+     * ];
+     *
+     * new ColorSpaceTransform(elements)}</pre>
+     *
+     * where {@code Nij} and {@code Dij} is the numerator and denominator for row {@code i} and
+     * column {@code j}.</p>
+     *
+     * @param elements An array of {@code 18} elements
+     *
+     * @throws IllegalArgumentException
+     *            if the count of {@code elements} is not {@code 18}
+     * @throws NullPointerException
+     *            if {@code elements} is {@code null}
+     */
+    public ColorSpaceTransform(int[] elements) {
+        checkNotNull(elements, "elements must not be null");
+        if (elements.length != COUNT_INT) {
+            throw new IllegalArgumentException("elements must be " + COUNT_INT + " length");
+        }
+
+        for (int i = 0; i < elements.length; ++i) {
+            checkNotNull(elements, "element " + i + " must not be null");
+        }
+
+        mElements = Arrays.copyOf(elements, elements.length);
+    }
+
+    /**
+     * Get an element of this matrix by its row and column.
+     *
+     * <p>The rows must be within the range [0, 3),
+     * and the column must be within the range [0, 3).</p>
+     *
+     * @return element (non-{@code null})
+     *
+     * @throws IllegalArgumentException if column or row was out of range
+     */
+    public Rational getElement(int column, int row) {
+        if (column < 0 || column >= COLUMNS) {
+            throw new IllegalArgumentException("column out of range");
+        } else if (row < 0 || row >= ROWS) {
+            throw new IllegalArgumentException("row out of range");
+        }
+
+        int numerator = mElements[row * ROWS * RATIONAL_SIZE + column + OFFSET_NUMERATOR];
+        int denominator = mElements[row * ROWS * RATIONAL_SIZE + column + OFFSET_DENOMINATOR];
+
+        return new Rational(numerator, denominator);
+    }
+
+    /**
+     * Copy the {@link Rational} elements in row-major order from this matrix into the destination.
+     *
+     * @param destination
+     *          an array big enough to hold at least {@code 9} elements after the
+     *          {@code offset}
+     * @param offset
+     *          a non-negative offset into the array
+     * @throws NullPointerException
+     *          If {@code destination} was {@code null}
+     * @throws ArrayIndexOutOfBoundsException
+     *          If there's not enough room to write the elements at the specified destination and
+     *          offset.
+     */
+    public void copyElements(Rational[] destination, int offset) {
+        checkArgumentNonnegative(offset, "offset must not be negative");
+        checkNotNull(destination, "destination must not be null");
+        if (destination.length + offset < COUNT) {
+            throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
+        }
+
+        for (int i = 0, j = 0; i < COUNT; ++i, j += RATIONAL_SIZE) {
+            int numerator = mElements[j + OFFSET_NUMERATOR];
+            int denominator = mElements[j + OFFSET_DENOMINATOR];
+
+            destination[i + offset] = new Rational(numerator, denominator);
+        }
+    }
+
+    /**
+     * Copy the {@link Rational} elements in row-major order from this matrix into the destination.
+     *
+     * <p>Each element is stored as a contiguous rational packed as a
+     * {@code (numerator, denominator)} pair of ints, identical to the
+     * {@link ColorSpaceTransform#ColorSpaceTransform(int[]) constructor}.</p>
+     *
+     * @param destination
+     *          an array big enough to hold at least {@code 18} elements after the
+     *          {@code offset}
+     * @param offset
+     *          a non-negative offset into the array
+     * @throws NullPointerException
+     *          If {@code destination} was {@code null}
+     * @throws ArrayIndexOutOfBoundsException
+     *          If there's not enough room to write the elements at the specified destination and
+     *          offset.
+     *
+     * @see ColorSpaceTransform#ColorSpaceTransform(int[])
+     */
+    public void copyElements(int[] destination, int offset) {
+        checkArgumentNonnegative(offset, "offset must not be negative");
+        checkNotNull(destination, "destination must not be null");
+        if (destination.length + offset < COUNT_INT) {
+            throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
+        }
+
+        // Manual copy faster than System#arraycopy for very small loops
+        for (int i = 0; i < COUNT_INT; ++i) {
+            destination[i + offset] = mElements[i];
+        }
+    }
+
+    /**
+     * Check if this {@link ColorSpaceTransform} is equal to another {@link ColorSpaceTransform}.
+     *
+     * <p>Two color space transforms are equal if and only if all of their elements are
+     * {@link Object#equals equal}.</p>
+     *
+     * @return {@code true} if the objects were equal, {@code false} otherwise
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof ColorSpaceTransform) {
+            final ColorSpaceTransform other = (ColorSpaceTransform) obj;
+            return Arrays.equals(mElements, other.mElements);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return HashCodeHelpers.hashCode(mElements);
+    }
+
+    private final int[] mElements;
+};
diff --git a/core/java/android/hardware/camera2/MeteringRectangle.java b/core/java/android/hardware/camera2/MeteringRectangle.java
new file mode 100644
index 0000000..ff7a745
--- /dev/null
+++ b/core/java/android/hardware/camera2/MeteringRectangle.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2;
+
+import android.util.Size;
+import static com.android.internal.util.Preconditions.*;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.camera2.impl.HashCodeHelpers;
+
+/**
+ * An immutable class to represent a rectangle {@code (x,y, width, height)} with an
+ * additional weight component.
+ *
+ * </p>The rectangle is defined to be inclusive of the specified coordinates.</p>
+ *
+ * <p>When used with a {@link CaptureRequest}, the coordinate system is based on the active pixel
+ * array, with {@code (0,0)} being the top-left pixel in the
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE active pixel array}, and
+ * {@code (android.sensor.info.activeArraySize.width - 1,
+ * android.sensor.info.activeArraySize.height - 1)}
+ * being the bottom-right pixel in the active pixel array.
+ * </p>
+ *
+ * <p>The metering weight is nonnegative.</p>
+ */
+public final class MeteringRectangle {
+
+    private final int mX;
+    private final int mY;
+    private final int mWidth;
+    private final int mHeight;
+    private final int mWeight;
+
+    /**
+     * Create a new metering rectangle.
+     *
+     * @param x coordinate >= 0
+     * @param y coordinate >= 0
+     * @param width width >= 0
+     * @param height height >= 0
+     * @param meteringWeight weight >= 0
+     *
+     * @throws IllegalArgumentException if any of the parameters were non-negative
+     */
+    public MeteringRectangle(int x, int y, int width, int height, int meteringWeight) {
+        mX = checkArgumentNonnegative(x, "x must be nonnegative");
+        mY = checkArgumentNonnegative(y, "y must be nonnegative");
+        mWidth = checkArgumentNonnegative(width, "width must be nonnegative");
+        mHeight = checkArgumentNonnegative(height, "height must be nonnegative");
+        mWeight = checkArgumentNonnegative(meteringWeight, "meteringWeight must be nonnegative");
+    }
+
+    /**
+     * Create a new metering rectangle.
+     *
+     * @param xy a non-{@code null} {@link Point} with both x,y >= 0
+     * @param dimensions a non-{@code null} {@link android.util.Size Size} with width, height >= 0
+     * @param meteringWeight weight >= 0
+     *
+     * @throws IllegalArgumentException if any of the parameters were non-negative
+     * @throws NullPointerException if any of the arguments were null
+     */
+    public MeteringRectangle(Point xy, Size dimensions, int meteringWeight) {
+        checkNotNull(xy, "xy must not be null");
+        checkNotNull(dimensions, "dimensions must not be null");
+
+        mX = checkArgumentNonnegative(xy.x, "x must be nonnegative");
+        mY = checkArgumentNonnegative(xy.y, "y must be nonnegative");
+        mWidth = checkArgumentNonnegative(dimensions.getWidth(), "width must be nonnegative");
+        mHeight = checkArgumentNonnegative(dimensions.getHeight(), "height must be nonnegative");
+        mWeight = checkArgumentNonnegative(meteringWeight, "meteringWeight must be nonnegative");
+    }
+
+    /**
+     * Create a new metering rectangle.
+     *
+     * @param rect a non-{@code null} rectangle with all x,y,w,h dimensions >= 0
+     * @param meteringWeight weight >= 0
+     *
+     * @throws IllegalArgumentException if any of the parameters were non-negative
+     * @throws NullPointerException if any of the arguments were null
+     */
+    public MeteringRectangle(Rect rect, int meteringWeight) {
+        checkNotNull(rect, "rect must not be null");
+
+        mX = checkArgumentNonnegative(rect.left, "rect.left must be nonnegative");
+        mY = checkArgumentNonnegative(rect.top, "rect.top must be nonnegative");
+        mWidth = checkArgumentNonnegative(rect.width(), "rect.width must be nonnegative");
+        mHeight = checkArgumentNonnegative(rect.height(), "rect.height must be nonnegative");
+        mWeight = checkArgumentNonnegative(meteringWeight, "meteringWeight must be nonnegative");
+    }
+
+    /**
+     * Return the X coordinate of the left side of the rectangle.
+     *
+     * @return x coordinate >= 0
+     */
+    public int getX() {
+        return mX;
+    }
+
+    /**
+     * Return the Y coordinate of the upper side of the rectangle.
+     *
+     * @return y coordinate >= 0
+     */
+    public int getY() {
+        return mY;
+    }
+
+    /**
+     * Return the width of the rectangle.
+     *
+     * @return width >= 0
+     */
+    public int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * Return the height of the rectangle.
+     *
+     * @return height >= 0
+     */
+    public int getHeight() {
+        return mHeight;
+    }
+
+    /**
+     * Return the metering weight of the rectangle.
+     *
+     * @return weight >= 0
+     */
+    public int getMeteringWeight() {
+        return mWeight;
+    }
+
+    /**
+     * Convenience method to create the upper-left (X,Y) coordinate as a {@link Point}.
+     *
+     * @return {@code (x,y)} point with both x,y >= 0
+     */
+    public Point getUpperLeftPoint() {
+        return new Point(mX, mY);
+    }
+
+    /**
+     * Convenience method to create the size from this metering rectangle.
+     *
+     * <p>This strips away the X,Y,weight from the rectangle.</p>
+     *
+     * @return a Size with non-negative width and height
+     */
+    public Size getSize() {
+        return new Size(mWidth, mHeight);
+    }
+
+    /**
+     * Convenience method to create a {@link Rect} from this metering rectangle.
+     *
+     * <p>This strips away the weight from the rectangle.</p>
+     *
+     * @return a {@link Rect} with non-negative x1, y1, x2, y2
+     */
+    public Rect getRect() {
+        return new Rect(mX, mY, mX + mWidth, mY + mHeight);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(final Object other) {
+        if (other instanceof MeteringRectangle) {
+            return equals(other);
+        }
+        return false;
+    }
+
+    /**
+     * Compare two metering rectangles to see if they are equal.
+     *
+     * Two weighted rectangles are only considered equal if each of their components
+     * (x, y, width, height, weight) is respectively equal.
+     *
+     * @param other Another MeteringRectangle
+     *
+     * @return {@code true} if the metering rectangles are equal, {@code false} otherwise
+     */
+    public boolean equals(final MeteringRectangle other) {
+        if (other == null) {
+            return false;
+        }
+
+        return (mX == other.mX
+                && mY == other.mY
+                && mWidth == other.mWidth
+                && mHeight == other.mHeight
+                && mWidth == other.mWidth);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return HashCodeHelpers.hashCode(mX, mY, mWidth, mHeight, mWeight);
+    }
+}
diff --git a/core/java/android/hardware/camera2/Size.java b/core/java/android/hardware/camera2/Size.java
index 45aaeae..9328a003 100644
--- a/core/java/android/hardware/camera2/Size.java
+++ b/core/java/android/hardware/camera2/Size.java
@@ -16,32 +16,55 @@
 
 package android.hardware.camera2;
 
+// TODO: Delete this class, since it was moved to android.util as public API
+
 /**
- * A simple immutable class for describing the dimensions of camera image
- * buffers.
+ * Immutable class for describing width and height dimensions in pixels.
+ *
+ * @hide
  */
 public final class Size {
     /**
-     * Create a new immutable Size instance
+     * Create a new immutable Size instance.
      *
-     * @param width The width to store in the Size instance
-     * @param height The height to store in the Size instance
+     * @param width The width of the size, in pixels
+     * @param height The height of the size, in pixels
      */
-    public Size(int width, int height) {
+    public Size(final int width, final int height) {
         mWidth = width;
         mHeight = height;
     }
 
+    /**
+     * Get the width of the size (in pixels).
+     * @return width
+     */
     public final int getWidth() {
         return mWidth;
     }
 
+    /**
+     * Get the height of the size (in pixels).
+     * @return height
+     */
     public final int getHeight() {
         return mHeight;
     }
 
+    /**
+     * Check if this size is equal to another size.
+     * <p>
+     * Two sizes are equal if and only if both their widths and heights are
+     * equal.
+     * </p>
+     * <p>
+     * A size object is never equal to any other type of object.
+     * </p>
+     *
+     * @return {@code true} if the objects were equal, {@code false} otherwise
+     */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(final Object obj) {
         if (obj == null) {
             return false;
         }
@@ -49,27 +72,29 @@
             return true;
         }
         if (obj instanceof Size) {
-            Size other = (Size) obj;
+            final Size other = (Size) obj;
             return mWidth == other.mWidth && mHeight == other.mHeight;
         }
         return false;
     }
 
+    /**
+     * Return the size represented as a string with the format {@code "WxH"}
+     *
+     * @return string representation of the size
+     */
     @Override
     public String toString() {
         return mWidth + "x" + mHeight;
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public int hashCode() {
-        final long INT_MASK = 0xffffffffL;
-
-        long asLong = INT_MASK & mWidth;
-        asLong <<= 32;
-
-        asLong |= (INT_MASK & mHeight);
-
-        return ((Long)asLong).hashCode();
+        // assuming most sizes are <2^16, doing a rotate will give us perfect hashing
+        return mHeight ^ ((mWidth << (Integer.SIZE / 2)) | (mWidth >>> (Integer.SIZE / 2)));
     }
 
     private final int mWidth;
diff --git a/core/java/android/hardware/camera2/impl/HashCodeHelpers.java b/core/java/android/hardware/camera2/impl/HashCodeHelpers.java
new file mode 100644
index 0000000..2d63827
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/HashCodeHelpers.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.impl;
+
+/**
+ * Provide hashing functions using the Modified Bernstein hash
+ */
+public final class HashCodeHelpers {
+
+    /**
+     * Hash every element uniformly using the Modified Bernstein hash.
+     *
+     * <p>Useful to implement a {@link Object#hashCode} for uniformly distributed data.</p>
+     *
+     * @param array a non-{@code null} array of integers
+     *
+     * @return the numeric hash code
+     */
+    public static int hashCode(int[] array) {
+        if (array == null) {
+            return 0;
+        }
+
+        /*
+         *  Note that we use 31 here instead of 33 since it's preferred in Effective Java
+         *  and used elsewhere in the runtime (e.g. Arrays#hashCode)
+         *
+         *  That being said 33 and 31 are nearly identical in terms of their usefulness
+         *  according to http://svn.apache.org/repos/asf/apr/apr/trunk/tables/apr_hash.c
+         */
+        int h = 1;
+        for (int x : array) {
+            // Strength reduction; in case the compiler has illusions about divisions being faster
+            h = ((h << 5) - h) ^ x; // (h * 31) XOR x
+        }
+
+        return h;
+    }
+
+    /**
+     * Hash every element uniformly using the Modified Bernstein hash.
+     *
+     * <p>Useful to implement a {@link Object#hashCode} for uniformly distributed data.</p>
+     *
+     * @param array a non-{@code null} array of floats
+     *
+     * @return the numeric hash code
+     */
+    public static int hashCode(float[] array) {
+        if (array == null) {
+            return 0;
+        }
+
+        int h = 1;
+        for (float f : array) {
+            int x = Float.floatToIntBits(f);
+            h = ((h << 5) - h) ^ x; // (h * 31) XOR x
+        }
+
+        return h;
+    }
+
+    /**
+     * Hash every element uniformly using the Modified Bernstein hash.
+     *
+     * <p>Useful to implement a {@link Object#hashCode} for uniformly distributed data.</p>
+     *
+     * @param array a non-{@code null} array of objects
+     *
+     * @return the numeric hash code
+     */
+    public static <T> int hashCode(T[] array) {
+        if (array == null) {
+            return 0;
+        }
+
+        int h = 1;
+        for (T o : array) {
+            int x = (o == null) ? 0 : o.hashCode();
+            h = ((h << 5) - h) ^ x; // (h * 31) XOR x
+        }
+
+        return h;
+    }
+
+    public static <T> int hashCode(T a) {
+        return (a == null) ? 0 : a.hashCode();
+    }
+
+    public static <T> int hashCode(T a, T b) {
+        int h = hashCode(a);
+
+        int x = (b == null) ? 0 : b.hashCode();
+        h = ((h << 5) - h) ^ x; // (h * 31) XOR x
+
+        return h;
+    }
+
+    public static <T> int hashCode(T a, T b, T c) {
+        int h = hashCode(a, b);
+
+        int x = (a == null) ? 0 : a.hashCode();
+        h = ((h << 5) - h) ^ x; // (h * 31) XOR x
+
+        return h;
+    }
+
+    public static int hashCode(int x) {
+        return hashCode(new int[] { x } );
+    }
+
+    public static int hashCode(int x, int y) {
+        return hashCode(new int[] { x, y } );
+    }
+
+    public static int hashCode(int x, int y, int z) {
+        return hashCode(new int[] { x, y, z } );
+    }
+
+    public static int hashCode(int x, int y, int z, int w) {
+        return hashCode(new int[] { x, y, z, w } );
+    }
+
+    public static int hashCode(int x, int y, int z, int w, int t) {
+        return hashCode(new int[] { x, y, z, w, t } );
+    }
+
+
+}
diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java
new file mode 100644
index 0000000..9a4bd4b
--- /dev/null
+++ b/core/java/android/util/Range.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import static com.android.internal.util.Preconditions.*;
+
+import android.hardware.camera2.impl.HashCodeHelpers;
+
+/**
+ * Immutable class for describing the range of two numeric values.
+ * <p>
+ * A range (or "interval") defines the inclusive boundaries around a contiguous span of
+ * values of some {@link Comparable} type; for example,
+ * "integers from 1 to 100 inclusive."
+ * </p>
+ * <p>
+ * All ranges are bounded, and the left side of the range is always {@code >=}
+ * the right side of the range.
+ * </p>
+ *
+ * <p>Although the implementation itself is immutable, there is no restriction that objects
+ * stored must also be immutable. If mutable objects are stored here, then the range
+ * effectively becomes mutable. </p>
+ */
+public final class Range<T extends Comparable<? super T>> {
+    /**
+     * Create a new immutable range.
+     *
+     * <p>
+     * The endpoints are {@code [lower, upper]}; that
+     * is the range is bounded. {@code lower} must be {@link Comparable#compareTo lesser or equal}
+     * to {@code upper}.
+     * </p>
+     *
+     * @param lower The lower endpoint (inclusive)
+     * @param upper The upper endpoint (inclusive)
+     *
+     * @throws NullPointerException if {@code lower} or {@code upper} is {@code null}
+     */
+    public Range(final T lower, final T upper) {
+        mLower = checkNotNull(lower, "lower must not be null");
+        mUpper = checkNotNull(upper, "upper must not be null");
+
+        if (lower.compareTo(upper) > 0) {
+            throw new IllegalArgumentException("lower must be less than or equal to upper");
+        }
+    }
+
+    /**
+     * Create a new immutable range, with the argument types inferred.
+     *
+     * <p>
+     * The endpoints are {@code [lower, upper]}; that
+     * is the range is bounded. {@code lower} must be {@link Comparable#compareTo lesser or equal}
+     * to {@code upper}.
+     * </p>
+     *
+     * @param lower The lower endpoint (inclusive)
+     * @param upper The upper endpoint (inclusive)
+     *
+     * @throws NullPointerException if {@code lower} or {@code upper} is {@code null}
+     */
+    public static <T extends Comparable<? super T>> Range<T> create(final T lower, final T upper) {
+        return new Range<T>(lower, upper);
+    }
+
+    /**
+     * Get the lower endpoint.
+     *
+     * @return a non-{@code null} {@code T} reference
+     */
+    public T getLower() {
+        return mLower;
+    }
+
+    /**
+     * Get the upper endpoint.
+     *
+     * @return a non-{@code null} {@code T} reference
+     */
+    public T getUpper() {
+        return mUpper;
+    }
+
+    /**
+     * Compare two ranges for equality.
+     *
+     * <p>A range is considered equal if and only if both the lower and upper endpoints
+     * are also equal.</p>
+     *
+     * @return {@code true} if the ranges are equal, {@code false} otherwise
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof Range) {
+            @SuppressWarnings("rawtypes")
+            final
+            Range other = (Range) obj;
+            return mLower.equals(other.mLower) && mUpper.equals(other.mUpper);
+        }
+        return false;
+    }
+
+    /**
+     * Return the range as a string representation {@code "[lower, upper]"}.
+     *
+     * @return string representation of the range
+     */
+    @Override
+    public String toString() {
+        return String.format("[%s, %s]", mLower, mUpper);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return HashCodeHelpers.hashCode(mLower, mUpper);
+    }
+
+    private final T mLower;
+    private final T mUpper;
+};
diff --git a/core/java/android/util/Size.java b/core/java/android/util/Size.java
new file mode 100644
index 0000000..ba1a35f
--- /dev/null
+++ b/core/java/android/util/Size.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Immutable class for describing width and height dimensions in pixels.
+ */
+public final class Size {
+    /**
+     * Create a new immutable Size instance.
+     *
+     * @param width The width of the size, in pixels
+     * @param height The height of the size, in pixels
+     */
+    public Size(int width, int height) {
+        mWidth = width;
+        mHeight = height;
+    }
+
+    /**
+     * Get the width of the size (in pixels).
+     * @return width
+     */
+    public int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * Get the height of the size (in pixels).
+     * @return height
+     */
+    public int getHeight() {
+        return mHeight;
+    }
+
+    /**
+     * Check if this size is equal to another size.
+     * <p>
+     * Two sizes are equal if and only if both their widths and heights are
+     * equal.
+     * </p>
+     * <p>
+     * A size object is never equal to any other type of object.
+     * </p>
+     *
+     * @return {@code true} if the objects were equal, {@code false} otherwise
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof Size) {
+            Size other = (Size) obj;
+            return mWidth == other.mWidth && mHeight == other.mHeight;
+        }
+        return false;
+    }
+
+    /**
+     * Return the size represented as a string with the format {@code "WxH"}
+     *
+     * @return string representation of the size
+     */
+    @Override
+    public String toString() {
+        return mWidth + "x" + mHeight;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        // assuming most sizes are <2^16, doing a rotate will give us perfect hashing
+        return mHeight ^ ((mWidth << (Integer.SIZE / 2)) | (mWidth >>> (Integer.SIZE / 2)));
+    }
+
+    private final int mWidth;
+    private final int mHeight;
+};
diff --git a/core/java/android/util/SizeF.java b/core/java/android/util/SizeF.java
new file mode 100644
index 0000000..0a8b4ed
--- /dev/null
+++ b/core/java/android/util/SizeF.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Immutable class for describing width and height dimensions in some arbitrary
+ * unit.
+ * <p>
+ * Width and height are finite values stored as a floating point representation.
+ * </p>
+ */
+public final class SizeF {
+    /**
+     * Create a new immutable SizeF instance.
+     *
+     * <p>Both the {@code width} and the {@code height} must be a finite number.
+     * In particular, {@code NaN} and positive/negative infinity are illegal values.</p>
+     *
+     * @param width The width of the size
+     * @param height The height of the size
+     *
+     * @throws IllegalArgumentException
+     *             if either {@code width} or {@code height} was not finite.
+     */
+    public SizeF(final float width, final float height) {
+        mWidth = checkArgumentFinite(width, "width");
+        mHeight = checkArgumentFinite(height, "height");
+    }
+
+    /**
+     * Get the width of the size (as an arbitrary unit).
+     * @return width
+     */
+    public float getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * Get the height of the size (as an arbitrary unit).
+     * @return height
+     */
+    public float getHeight() {
+        return mHeight;
+    }
+
+    /**
+     * Check if this size is equal to another size.
+     *
+     * <p>Two sizes are equal if and only if both their widths and heights are the same.</p>
+     *
+     * <p>For this purpose, the width/height float values are considered to be the same if and only
+     * if the method {@link Float#floatToIntBits(float)} returns the identical {@code int} value
+     * when applied to each.</p>
+     *
+     * @return {@code true} if the objects were equal, {@code false} otherwise
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof SizeF) {
+            final SizeF other = (SizeF) obj;
+            return mWidth == other.mWidth && mHeight == other.mHeight;
+        }
+        return false;
+    }
+
+    /**
+     * Return the size represented as a string with the format {@code "WxH"}
+     *
+     * @return string representation of the size
+     */
+    @Override
+    public String toString() {
+        return mWidth + "x" + mHeight;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return Float.floatToIntBits(mWidth) ^ Float.floatToIntBits(mHeight);
+    }
+
+    private final float mWidth;
+    private final float mHeight;
+};
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index a54b364..f6722a6 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -30,7 +30,7 @@
      * @return the non-null reference that was validated
      * @throws NullPointerException if {@code reference} is null
      */
-    public static <T> T checkNotNull(T reference) {
+    public static <T> T checkNotNull(final T reference) {
         if (reference == null) {
             throw new NullPointerException();
         }
@@ -47,7 +47,7 @@
      * @return the non-null reference that was validated
      * @throws NullPointerException if {@code reference} is null
      */
-    public static <T> T checkNotNull(T reference, Object errorMessage) {
+    public static <T> T checkNotNull(final T reference, final Object errorMessage) {
         if (reference == null) {
             throw new NullPointerException(String.valueOf(errorMessage));
         }
@@ -61,7 +61,7 @@
      * @param expression a boolean expression
      * @throws IllegalStateException if {@code expression} is false
      */
-    public static void checkState(boolean expression) {
+    public static void checkState(final boolean expression) {
         if (!expression) {
             throw new IllegalStateException();
         }
@@ -71,11 +71,178 @@
      * Check the requested flags, throwing if any requested flags are outside
      * the allowed set.
      */
-    public static void checkFlagsArgument(int requestedFlags, int allowedFlags) {
+    public static void checkFlagsArgument(final int requestedFlags, final int allowedFlags) {
         if ((requestedFlags & allowedFlags) != requestedFlags) {
             throw new IllegalArgumentException("Requested flags 0x"
                     + Integer.toHexString(requestedFlags) + ", but only 0x"
                     + Integer.toHexString(allowedFlags) + " are allowed");
         }
     }
+
+    /**
+     * Ensures that that the argument numeric value is non-negative.
+     *
+     * @param value a numeric int value
+     * @param errorMessage the exception message to use if the check fails
+     * @return the validated numeric value
+     * @throws IllegalArgumentException if {@code value} was negative
+     */
+    public static int checkArgumentNonnegative(final int value, final String errorMessage) {
+        if (value < 0) {
+            throw new IllegalArgumentException(errorMessage);
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that that the argument numeric value is non-negative.
+     *
+     * @param value a numeric long value
+     * @param errorMessage the exception message to use if the check fails
+     * @return the validated numeric value
+     * @throws IllegalArgumentException if {@code value} was negative
+     */
+    public static long checkArgumentNonnegative(final long value, final String errorMessage) {
+        if (value < 0) {
+            throw new IllegalArgumentException(errorMessage);
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that that the argument numeric value is positive.
+     *
+     * @param value a numeric int value
+     * @param errorMessage the exception message to use if the check fails
+     * @return the validated numeric value
+     * @throws IllegalArgumentException if {@code value} was not positive
+     */
+    public static int checkArgumentPositive(final int value, final String errorMessage) {
+        if (value <= 0) {
+            throw new IllegalArgumentException(errorMessage);
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that the argument floating point value is a finite number.
+     *
+     * <p>A finite number is defined to be both representable (that is, not NaN) and
+     * not infinite (that is neither positive or negative infinity).</p>
+     *
+     * @param value a floating point value
+     * @param valueName the name of the argument to use if the check fails
+     *
+     * @return the validated floating point value
+     *
+     * @throws IllegalArgumentException if {@code value} was not finite
+     */
+    public static float checkArgumentFinite(final float value, final String valueName) {
+        if (Float.isNaN(value)) {
+            throw new IllegalArgumentException(valueName + " must not be NaN");
+        } else if (Float.isInfinite(value)) {
+            throw new IllegalArgumentException(valueName + " must not be infinite");
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that the argument floating point value is within the inclusive range.
+     *
+     * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
+     * will always be out of range.</p>
+     *
+     * @param value a floating point value
+     * @param lower the lower endpoint of the inclusive range
+     * @param upper the upper endpoint of the inclusive range
+     * @param valueName the name of the argument to use if the check fails
+     *
+     * @return the validated floating point value
+     *
+     * @throws IllegalArgumentException if {@code value} was not within the range
+     */
+    public static float checkArgumentInRange(float value, float lower, float upper,
+            String valueName) {
+        if (Float.isNaN(value)) {
+            throw new IllegalArgumentException(valueName + " must not be NaN");
+        } else if (value < lower) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "%s is out of range of [%f, %f] (too low)", valueName, lower, upper));
+        } else if (value > upper) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "%s is out of range of [%f, %f] (too high)", valueName, lower, upper));
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that the array is not {@code null}, and none if its elements are {@code null}.
+     *
+     * @param value an array of boxed objects
+     * @param valueName the name of the argument to use if the check fails
+     *
+     * @return the validated array
+     *
+     * @throws NullPointerException if the {@code value} or any of its elements were {@code null}
+     */
+    public static <T> T[] checkArrayElementsNotNull(final T[] value, final String valueName) {
+        if (value == null) {
+            throw new NullPointerException(valueName + " must not be null");
+        }
+
+        for (int i = 0; i < value.length; ++i) {
+            if (value[i] == null) {
+                throw new NullPointerException(
+                        String.format("%s[%d] must not be null", valueName, i));
+            }
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that all elements in the argument floating point array are within the inclusive range
+     *
+     * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
+     * will always be out of range.</p>
+     *
+     * @param value a floating point array of values
+     * @param lower the lower endpoint of the inclusive range
+     * @param upper the upper endpoint of the inclusive range
+     * @param valueName the name of the argument to use if the check fails
+     *
+     * @return the validated floating point value
+     *
+     * @throws IllegalArgumentException if any of the elements in {@code value} were out of range
+     * @throws NullPointerException if the {@code value} was {@code null}
+     */
+    public static float[] checkArrayElementsInRange(float[] value, float lower, float upper,
+            String valueName) {
+        checkNotNull(value, valueName + " must not be null");
+
+        for (int i = 0; i < value.length; ++i) {
+            float v = value[i];
+
+            if (Float.isNaN(v)) {
+                throw new IllegalArgumentException(valueName + "[" + i + "] must not be NaN");
+            } else if (v < lower) {
+                throw new IllegalArgumentException(
+                        String.format("%s[%d] is out of range of [%f, %f] (too low)",
+                                valueName, i, lower, upper));
+            } else if (v > upper) {
+                throw new IllegalArgumentException(
+                        String.format("%s[%d] is out of range of [%f, %f] (too high)",
+                                valueName, i, lower, upper));
+            }
+        }
+
+        return value;
+    }
 }