The distinction is only important if {@code T} is a generic, e.g.
+ * {@code Range} since the nested type will be erased.
+ *
* @hide
*/
public final Class getType() {
+ // TODO: remove this; other places should use #getTypeReference() instead
return mType;
}
+
+ /**
+ * Get the type reference backing the type {@code T} for this key.
+ *
+ *
The distinction is only important if {@code T} is a generic, e.g.
+ * {@code Range} since the nested type will be retained.
+ *
+ * @hide
+ */
+ public final TypeReference getTypeReference() {
+ return mTypeReference;
+ }
}
/*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
diff --git a/core/java/android/hardware/camera2/ColorSpaceTransform.java b/core/java/android/hardware/camera2/ColorSpaceTransform.java
index 9912e4ba5c19f..5e4c0a28166bc 100644
--- a/core/java/android/hardware/camera2/ColorSpaceTransform.java
+++ b/core/java/android/hardware/camera2/ColorSpaceTransform.java
@@ -17,7 +17,8 @@
package android.hardware.camera2;
import static com.android.internal.util.Preconditions.*;
-import android.hardware.camera2.impl.HashCodeHelpers;
+
+import android.hardware.camera2.utils.HashCodeHelpers;
import java.util.Arrays;
diff --git a/core/java/android/hardware/camera2/LensShadingMap.java b/core/java/android/hardware/camera2/LensShadingMap.java
index 2c224f645ffc1..2b0108ce1ef3c 100644
--- a/core/java/android/hardware/camera2/LensShadingMap.java
+++ b/core/java/android/hardware/camera2/LensShadingMap.java
@@ -19,7 +19,7 @@ package android.hardware.camera2;
import static com.android.internal.util.Preconditions.*;
import static android.hardware.camera2.RggbChannelVector.*;
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
import java.util.Arrays;
diff --git a/core/java/android/hardware/camera2/MeteringRectangle.java b/core/java/android/hardware/camera2/MeteringRectangle.java
index ff7a745b75a69..bb8e5b1b02458 100644
--- a/core/java/android/hardware/camera2/MeteringRectangle.java
+++ b/core/java/android/hardware/camera2/MeteringRectangle.java
@@ -20,7 +20,7 @@ import static com.android.internal.util.Preconditions.*;
import android.graphics.Point;
import android.graphics.Rect;
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
/**
* An immutable class to represent a rectangle {@code (x,y, width, height)} with an
@@ -186,10 +186,7 @@ public final class MeteringRectangle {
*/
@Override
public boolean equals(final Object other) {
- if (other instanceof MeteringRectangle) {
- return equals(other);
- }
- return false;
+ return other instanceof MeteringRectangle && equals((MeteringRectangle)other);
}
/**
diff --git a/core/java/android/hardware/camera2/Rational.java b/core/java/android/hardware/camera2/Rational.java
index 77b8c2685441d..693ee2b146260 100644
--- a/core/java/android/hardware/camera2/Rational.java
+++ b/core/java/android/hardware/camera2/Rational.java
@@ -91,14 +91,14 @@ public final class Rational {
*
A reduced form of a Rational is calculated by dividing both the numerator and the
* denominator by their greatest common divisor.
*
- *
+ *
{@code
* (new Rational(1, 2)).equals(new Rational(1, 2)) == true // trivially true
* (new Rational(2, 3)).equals(new Rational(1, 2)) == false // trivially false
* (new Rational(1, 2)).equals(new Rational(2, 4)) == true // true after reduction
* (new Rational(0, 0)).equals(new Rational(0, 0)) == true // NaN.equals(NaN)
* (new Rational(1, 0)).equals(new Rational(5, 0)) == true // both are +infinity
* (new Rational(1, 0)).equals(new Rational(-1, 0)) == false // +infinity != -infinity
- *
+ * }
*
* @param obj a reference to another object
*
@@ -159,16 +159,15 @@ public final class Rational {
return (float) mNumerator / (float) mDenominator;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public int hashCode() {
- final long INT_MASK = 0xffffffffL;
+ // Bias the hash code for the first (2^16) values for both numerator and denominator
+ int numeratorFlipped = mNumerator << 16 | mNumerator >>> 16;
- long asLong = INT_MASK & mNumerator;
- asLong <<= 32;
-
- asLong |= (INT_MASK & mDenominator);
-
- return ((Long)asLong).hashCode();
+ return mDenominator ^ numeratorFlipped;
}
/**
diff --git a/core/java/android/hardware/camera2/ReprocessFormatsMap.java b/core/java/android/hardware/camera2/ReprocessFormatsMap.java
index c6c59d433770d..894a499168eab 100644
--- a/core/java/android/hardware/camera2/ReprocessFormatsMap.java
+++ b/core/java/android/hardware/camera2/ReprocessFormatsMap.java
@@ -18,7 +18,7 @@ package android.hardware.camera2;
import static com.android.internal.util.Preconditions.*;
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
import java.util.Arrays;
diff --git a/core/java/android/hardware/camera2/StreamConfiguration.java b/core/java/android/hardware/camera2/StreamConfiguration.java
index c53dd7ccdd6a4..a514034aa3dbf 100644
--- a/core/java/android/hardware/camera2/StreamConfiguration.java
+++ b/core/java/android/hardware/camera2/StreamConfiguration.java
@@ -20,7 +20,7 @@ import static com.android.internal.util.Preconditions.*;
import static android.hardware.camera2.StreamConfigurationMap.checkArgumentFormatInternal;
import android.graphics.ImageFormat;
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
import android.util.Size;
/**
@@ -57,7 +57,7 @@ public final class StreamConfiguration {
final int format, final int width, final int height, final boolean input) {
mFormat = checkArgumentFormatInternal(format);
mWidth = checkArgumentPositive(width, "width must be positive");
- mHeight = checkArgumentPositive(width, "height must be positive");
+ mHeight = checkArgumentPositive(height, "height must be positive");
mInput = input;
}
diff --git a/core/java/android/hardware/camera2/StreamConfigurationDuration.java b/core/java/android/hardware/camera2/StreamConfigurationDuration.java
index 189ae6240b273..6a3115658ff31 100644
--- a/core/java/android/hardware/camera2/StreamConfigurationDuration.java
+++ b/core/java/android/hardware/camera2/StreamConfigurationDuration.java
@@ -20,7 +20,7 @@ import static com.android.internal.util.Preconditions.*;
import static android.hardware.camera2.StreamConfigurationMap.checkArgumentFormatInternal;
import android.graphics.ImageFormat;
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
import android.util.Size;
/**
@@ -54,7 +54,7 @@ public final class StreamConfigurationDuration {
final int format, final int width, final int height, final long durationNs) {
mFormat = checkArgumentFormatInternal(format);
mWidth = checkArgumentPositive(width, "width must be positive");
- mHeight = checkArgumentPositive(width, "height must be positive");
+ mHeight = checkArgumentPositive(height, "height must be positive");
mDurationNs = checkArgumentNonnegative(durationNs, "durationNs must be non-negative");
}
diff --git a/core/java/android/hardware/camera2/StreamConfigurationMap.java b/core/java/android/hardware/camera2/StreamConfigurationMap.java
index e24fd1b6a0b18..5ddd7d61a85db 100644
--- a/core/java/android/hardware/camera2/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/StreamConfigurationMap.java
@@ -18,7 +18,7 @@ package android.hardware.camera2;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
import android.view.Surface;
import android.util.Size;
diff --git a/core/java/android/hardware/camera2/TonemapCurve.java b/core/java/android/hardware/camera2/TonemapCurve.java
index ee20d68d4cf2c..2958ebf1f54c2 100644
--- a/core/java/android/hardware/camera2/TonemapCurve.java
+++ b/core/java/android/hardware/camera2/TonemapCurve.java
@@ -19,7 +19,7 @@ package android.hardware.camera2;
import static com.android.internal.util.Preconditions.*;
import android.graphics.PointF;
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
import java.util.Arrays;
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index c5e5753b1a142..9a06e9721d0b4 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -23,16 +23,33 @@ import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.Face;
-import android.hardware.camera2.Rational;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.marshal.MarshalRegistry;
+import android.hardware.camera2.marshal.impl.MarshalQueryableArray;
+import android.hardware.camera2.marshal.impl.MarshalQueryableBoolean;
+import android.hardware.camera2.marshal.impl.MarshalQueryableColorSpaceTransform;
+import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
+import android.hardware.camera2.marshal.impl.MarshalQueryableMeteringRectangle;
+import android.hardware.camera2.marshal.impl.MarshalQueryableNativeByteToInteger;
+import android.hardware.camera2.marshal.impl.MarshalQueryableParcelable;
+import android.hardware.camera2.marshal.impl.MarshalQueryablePrimitive;
+import android.hardware.camera2.marshal.impl.MarshalQueryableRange;
+import android.hardware.camera2.marshal.impl.MarshalQueryableRect;
+import android.hardware.camera2.marshal.impl.MarshalQueryableReprocessFormatsMap;
+import android.hardware.camera2.marshal.impl.MarshalQueryableRggbChannelVector;
+import android.hardware.camera2.marshal.impl.MarshalQueryableSize;
+import android.hardware.camera2.marshal.impl.MarshalQueryableSizeF;
+import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfiguration;
+import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration;
+import android.hardware.camera2.marshal.impl.MarshalQueryableString;
import android.os.Parcelable;
import android.os.Parcel;
import android.util.Log;
-import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
-import java.util.HashMap;
/**
* Implementation of camera metadata marshal/unmarshal across Binder to
@@ -89,7 +106,6 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable {
nativeWriteToParcel(dest);
}
- @SuppressWarnings("unchecked")
@Override
public T get(Key key) {
T value = getOverride(key);
@@ -169,275 +185,6 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable {
mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final
}
- private static int getTypeSize(int nativeType) {
- switch(nativeType) {
- case TYPE_BYTE:
- return 1;
- case TYPE_INT32:
- case TYPE_FLOAT:
- return 4;
- case TYPE_INT64:
- case TYPE_DOUBLE:
- case TYPE_RATIONAL:
- return 8;
- }
-
- throw new UnsupportedOperationException("Unknown type, can't get size "
- + nativeType);
- }
-
- private static Class> getExpectedType(int nativeType) {
- switch(nativeType) {
- case TYPE_BYTE:
- return Byte.TYPE;
- case TYPE_INT32:
- return Integer.TYPE;
- case TYPE_FLOAT:
- return Float.TYPE;
- case TYPE_INT64:
- return Long.TYPE;
- case TYPE_DOUBLE:
- return Double.TYPE;
- case TYPE_RATIONAL:
- return Rational.class;
- }
-
- throw new UnsupportedOperationException("Unknown type, can't map to Java type "
- + nativeType);
- }
-
- @SuppressWarnings("unchecked")
- private static int packSingleNative(T value, ByteBuffer buffer, Class type,
- int nativeType, boolean sizeOnly) {
-
- if (!sizeOnly) {
- /**
- * Rewrite types when the native type doesn't match the managed type
- * - Boolean -> Byte
- * - Integer -> Byte
- */
-
- if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
- // Since a boolean can't be cast to byte, and we don't want to use putBoolean
- boolean asBool = (Boolean) value;
- byte asByte = (byte) (asBool ? 1 : 0);
- value = (T) (Byte) asByte;
- } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
- int asInt = (Integer) value;
- byte asByte = (byte) asInt;
- value = (T) (Byte) asByte;
- } else if (type != getExpectedType(nativeType)) {
- throw new UnsupportedOperationException("Tried to pack a type of " + type +
- " but we expected the type to be " + getExpectedType(nativeType));
- }
-
- if (nativeType == TYPE_BYTE) {
- buffer.put((Byte) value);
- } else if (nativeType == TYPE_INT32) {
- buffer.putInt((Integer) value);
- } else if (nativeType == TYPE_FLOAT) {
- buffer.putFloat((Float) value);
- } else if (nativeType == TYPE_INT64) {
- buffer.putLong((Long) value);
- } else if (nativeType == TYPE_DOUBLE) {
- buffer.putDouble((Double) value);
- } else if (nativeType == TYPE_RATIONAL) {
- Rational r = (Rational) value;
- buffer.putInt(r.getNumerator());
- buffer.putInt(r.getDenominator());
- }
-
- }
-
- return getTypeSize(nativeType);
- }
-
- @SuppressWarnings({"unchecked", "rawtypes"})
- private static int packSingle(T value, ByteBuffer buffer, Class type, int nativeType,
- boolean sizeOnly) {
-
- int size = 0;
-
- if (type.isPrimitive() || type == Rational.class) {
- size = packSingleNative(value, buffer, type, nativeType, sizeOnly);
- } else if (type.isEnum()) {
- size = packEnum((Enum)value, buffer, (Class)type, nativeType, sizeOnly);
- } else if (type.isArray()) {
- size = packArray(value, buffer, type, nativeType, sizeOnly);
- } else {
- size = packClass(value, buffer, type, nativeType, sizeOnly);
- }
-
- return size;
- }
-
- private static > int packEnum(T value, ByteBuffer buffer, Class type,
- int nativeType, boolean sizeOnly) {
-
- // TODO: add support for enums with their own values.
- return packSingleNative(getEnumValue(value), buffer, Integer.TYPE, nativeType, sizeOnly);
- }
-
- @SuppressWarnings("unchecked")
- private static int packClass(T value, ByteBuffer buffer, Class type, int nativeType,
- boolean sizeOnly) {
-
- MetadataMarshalClass marshaler = getMarshaler(type, nativeType);
- if (marshaler == null) {
- throw new IllegalArgumentException(String.format("Unknown Key type: %s", type));
- }
-
- return marshaler.marshal(value, buffer, nativeType, sizeOnly);
- }
-
- private static int packArray(T value, ByteBuffer buffer, Class type, int nativeType,
- boolean sizeOnly) {
-
- int size = 0;
- int arrayLength = Array.getLength(value);
-
- @SuppressWarnings("unchecked")
- Class
+ *
+ *
This doesn't strictly follow the java understanding of primitive since
+ * boxed objects are included, Rational is included, and other types such as char and
+ * short are not included.
+ *
+ * @param klass a {@link Class} instance; using {@code null} will return {@code false}
+ * @return {@code true} if primitive, {@code false} otherwise
+ */
+ public static boolean isPrimitiveClass(Class klass) {
+ if (klass == null) {
+ return false;
+ }
+
+ if (klass == byte.class || klass == Byte.class) {
+ return true;
+ } else if (klass == int.class || klass == Integer.class) {
+ return true;
+ } else if (klass == float.class || klass == Float.class) {
+ return true;
+ } else if (klass == long.class || klass == Long.class) {
+ return true;
+ } else if (klass == double.class || klass == Double.class) {
+ return true;
+ } else if (klass == Rational.class) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Wrap {@code klass} with its wrapper variant if it was a {@code Class} corresponding
+ * to a Java primitive.
+ *
+ *
Non-primitive classes are passed through as-is.
+ *
+ *
For example, for a primitive {@code int.class => Integer.class},
+ * but for a non-primitive {@code Rational.class => Rational.class}.
+ *
+ * @param klass a {@code Class} reference
+ *
+ * @return wrapped class object, or same class object if non-primitive
+ */
+ @SuppressWarnings("unchecked")
+ public static Class wrapClassIfPrimitive(Class klass) {
+ if (klass == byte.class) {
+ return (Class)Byte.class;
+ } else if (klass == int.class) {
+ return (Class)Integer.class;
+ } else if (klass == float.class) {
+ return (Class)Float.class;
+ } else if (klass == long.class) {
+ return (Class)Long.class;
+ } else if (klass == double.class) {
+ return (Class)Double.class;
+ }
+
+ return klass;
+ }
+
+ /**
+ * Return a human-readable representation of the {@code nativeType}, e.g. "TYPE_INT32"
+ *
+ *
Out-of-range values return a string with "UNKNOWN" as the prefix.
+ *
+ * @param nativeType the native type
+ *
+ * @return human readable type name
+ */
+ public static String toStringNativeType(int nativeType) {
+ switch (nativeType) {
+ case TYPE_BYTE:
+ return "TYPE_BYTE";
+ case TYPE_INT32:
+ return "TYPE_INT32";
+ case TYPE_FLOAT:
+ return "TYPE_FLOAT";
+ case TYPE_INT64:
+ return "TYPE_INT64";
+ case TYPE_DOUBLE:
+ return "TYPE_DOUBLE";
+ case TYPE_RATIONAL:
+ return "TYPE_RATIONAL";
+ }
+
+ return "UNKNOWN(" + nativeType + ")";
+ }
+
+ /**
+ * Ensure that the {@code nativeType} is one of the native types supported
+ * by {@link CameraMetadataNative}.
+ *
+ * @param nativeType the native type
+ *
+ * @return the native type
+ *
+ * @throws UnsupportedOperationException if the native type was invalid
+ */
+ public static int checkNativeType(int nativeType) {
+ switch (nativeType) {
+ case TYPE_BYTE:
+ case TYPE_INT32:
+ case TYPE_FLOAT:
+ case TYPE_INT64:
+ case TYPE_DOUBLE:
+ case TYPE_RATIONAL:
+ return nativeType;
+ }
+
+ throw new UnsupportedOperationException("Unknown nativeType " + nativeType);
+ }
+
+ /**
+ * Ensure that the expected and actual native types are equal.
+ *
+ * @param expectedNativeType the expected native type
+ * @param actualNativeType the actual native type
+ * @return the actual native type
+ *
+ * @throws UnsupportedOperationException if the types are not equal
+ */
+ public static int checkNativeTypeEquals(int expectedNativeType, int actualNativeType) {
+ if (expectedNativeType != actualNativeType) {
+ throw new UnsupportedOperationException(
+ String.format("Expected native type %d, but got %d",
+ expectedNativeType, actualNativeType));
+ }
+
+ return actualNativeType;
+ }
+
+ private MarshalHelpers() {
+ throw new AssertionError();
+ }
+}
diff --git a/core/java/android/hardware/camera2/marshal/MarshalQueryable.java b/core/java/android/hardware/camera2/marshal/MarshalQueryable.java
new file mode 100644
index 0000000000000..35fed1f196f86
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/MarshalQueryable.java
@@ -0,0 +1,63 @@
+/*
+ * 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.marshal;
+
+import android.hardware.camera2.utils.TypeReference;
+
+/**
+ * Query if a marshaler can marshal to/from a particular native and managed type; if it supports
+ * the combination, allow creating a marshaler instance to do the serialization.
+ *
+ *
Not all queryable instances will support exactly one combination. Some, such as the
+ * primitive queryable will support all primitive to/from managed mappings (as long as they are
+ * 1:1). Others, such as the rectangle queryable will only support integer to rectangle mappings.
+ *
+ *
+ *
Yet some others are codependent on other queryables; e.g. array queryables might only support
+ * a type map for {@code T[]} if another queryable exists with support for the component type
+ * {@code T}.
+ */
+public interface MarshalQueryable {
+ /**
+ * Create a marshaler between the selected managed and native type.
+ *
+ *
This marshaler instance is only good for that specific type mapping; and will refuse
+ * to map other managed types, other native types, or an other combination that isn't
+ * this exact one.
+ *
+ * @param managedType a managed type reference
+ * @param nativeType the native type, e.g.
+ * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}
+ * @return
+ *
+ * @throws UnsupportedOperationException
+ * if {@link #isTypeMappingSupported} returns {@code false}
+ */
+ public Marshaler createMarshaler(
+ TypeReference managedType, int nativeType);
+
+ /**
+ * Determine whether or not this query marshal is able to create a marshaler that will
+ * support the managed type and native type mapping.
+ *
+ *
If this returns {@code true}, then a marshaler can be instantiated by
+ * {@link #createMarshaler} that will marshal data to/from the native type
+ * from/to the managed type.
+ *
+ *
Most marshalers are likely to only support one type map.
+ */
+ public boolean isTypeMappingSupported(TypeReference managedType, int nativeType);
+}
diff --git a/core/java/android/hardware/camera2/marshal/MarshalRegistry.java b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java
new file mode 100644
index 0000000000000..92d9057589ced
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java
@@ -0,0 +1,133 @@
+/*
+ * 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.marshal;
+
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Registry of supported marshalers; add new query-able marshalers or lookup existing ones.
+ */
+public class MarshalRegistry {
+
+ /**
+ * Register a marshal queryable for the managed type {@code T}.
+ *
+ *
Multiple marshal queryables for the same managed type {@code T} may be registered;
+ * this is desirable if they support different native types (e.g. marshaler 1 supports
+ * {@code Integer <-> TYPE_INT32}, marshaler 2 supports {@code Integer <-> TYPE_BYTE}.
+ *
+ * @param queryable a non-{@code null} marshal queryable that supports marshaling {@code T}
+ */
+ public static void registerMarshalQueryable(MarshalQueryable queryable) {
+ sRegisteredMarshalQueryables.add(queryable);
+ }
+
+ /**
+ * Lookup a marshaler between {@code T} and {@code nativeType}.
+ *
+ *
Marshalers are looked up in the order they were registered; earlier registered
+ * marshal queriers get priority.
+ *
+ * @param typeToken The compile-time type reference for {@code T}
+ * @param nativeType The native type, e.g. {@link CameraMetadataNative#TYPE_BYTE TYPE_BYTE}
+ * @return marshaler a non-{@code null} marshaler that supports marshaling the type combo
+ *
+ * @throws UnsupportedOperationException If no marshaler matching the args could be found
+ */
+ @SuppressWarnings("unchecked")
+ public static Marshaler getMarshaler(TypeReference typeToken, int nativeType) {
+ // TODO: can avoid making a new token each time by code-genning
+ // the list of type tokens and native types from the keys (at the call sites)
+ MarshalToken marshalToken = new MarshalToken(typeToken, nativeType);
+
+ /*
+ * Marshalers are instantiated lazily once they are looked up; successive lookups
+ * will not instantiate new marshalers.
+ */
+ Marshaler marshaler =
+ (Marshaler) sMarshalerMap.get(marshalToken);
+
+ if (sRegisteredMarshalQueryables.size() == 0) {
+ throw new AssertionError("No available query marshalers registered");
+ }
+
+ if (marshaler == null) {
+ // Query each marshaler to see if they support the native/managed type combination
+ for (MarshalQueryable> potentialMarshaler : sRegisteredMarshalQueryables) {
+
+ MarshalQueryable castedPotential =
+ (MarshalQueryable)potentialMarshaler;
+
+ if (castedPotential.isTypeMappingSupported(typeToken, nativeType)) {
+ marshaler = castedPotential.createMarshaler(typeToken, nativeType);
+ break;
+ }
+ }
+ }
+
+ if (marshaler == null) {
+ throw new UnsupportedOperationException(
+ "Could not find marshaler that matches the requested " +
+ "combination of type reference " +
+ typeToken + " and native type " +
+ MarshalHelpers.toStringNativeType(nativeType));
+ }
+
+ sMarshalerMap.put(marshalToken, marshaler);
+
+ return marshaler;
+ }
+
+ private static class MarshalToken {
+ public MarshalToken(TypeReference typeReference, int nativeType) {
+ this.typeReference = typeReference;
+ this.nativeType = nativeType;
+ }
+
+ final TypeReference typeReference;
+ final int nativeType;
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof MarshalToken>) {
+ MarshalToken> otherToken = (MarshalToken>)other;
+ return typeReference.equals(otherToken.typeReference) &&
+ nativeType == otherToken.nativeType;
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return typeReference.hashCode() ^ nativeType;
+ }
+ }
+
+ private static List> sRegisteredMarshalQueryables =
+ new ArrayList>();
+ private static HashMap, Marshaler>> sMarshalerMap =
+ new HashMap, Marshaler>>();
+
+ private MarshalRegistry() {
+ throw new AssertionError();
+ }
+}
diff --git a/core/java/android/hardware/camera2/marshal/Marshaler.java b/core/java/android/hardware/camera2/marshal/Marshaler.java
new file mode 100644
index 0000000000000..eb0ad156c3cb9
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/Marshaler.java
@@ -0,0 +1,148 @@
+/*
+ * 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.marshal;
+
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Base class to marshal data to/from managed/native metadata byte buffers.
+ *
+ *
This class should not be created directly; an instance of it can be obtained
+ * using {@link MarshalQueryable#createMarshaler} for the same type {@code T} if the native type
+ * mapping for {@code T} {@link MarshalQueryable#isTypeMappingSupported supported}.
+ *
+ * @param the compile-time managed type
+ */
+public abstract class Marshaler {
+
+ protected final TypeReference mTypeReference;
+ protected final int mNativeType;
+
+ /**
+ * Instantiate a marshaler between a single managed/native type combination.
+ *
+ *
This particular managed/native type combination must be supported by
+ * {@link #isTypeMappingSupported}.
+ *
+ * @param query an instance of {@link MarshalQueryable}
+ * @param typeReference the managed type reference
+ * Must be one for which {@link #isTypeMappingSupported} returns {@code true}
+ * @param nativeType the native type, e.g.
+ * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}.
+ * Must be one for which {@link #isTypeMappingSupported} returns {@code true}.
+ *
+ * @throws NullPointerException if any args were {@code null}
+ * @throws UnsupportedOperationException if the type mapping was not supported
+ */
+ protected Marshaler(
+ MarshalQueryable query, TypeReference typeReference, int nativeType) {
+ mTypeReference = checkNotNull(typeReference, "typeReference must not be null");
+ mNativeType = checkNativeType(nativeType);
+
+ if (!query.isTypeMappingSupported(typeReference, nativeType)) {
+ throw new UnsupportedOperationException(
+ "Unsupported type marshaling for managed type "
+ + typeReference + " and native type "
+ + MarshalHelpers.toStringNativeType(nativeType));
+ }
+ }
+
+ /**
+ * Marshal the specified object instance (value) into a byte buffer.
+ *
+ *
Upon completion, the {@link ByteBuffer#position()} will have advanced by
+ * the {@link #calculateMarshalSize marshal size} of {@code value}.
+ *
+ * @param value the value of type T that we wish to write into the byte buffer
+ * @param buffer the byte buffer into which the marshaled object will be written
+ */
+ public abstract void marshal(T value, ByteBuffer buffer);
+
+ /**
+ * Get the size in bytes for how much space would be required to write this {@code value}
+ * into a byte buffer using the given {@code nativeType}.
+ *
+ *
If the size of this {@code T} instance when serialized into a buffer is always constant,
+ * then this method will always return the same value (and particularly, it will return
+ * an equivalent value to {@link #getNativeSize()}.
+ *
+ *
Overriding this method is a must when the size is {@link NATIVE_SIZE_DYNAMIC dynamic}.
+ *
+ * @param value the value of type T that we wish to write into the byte buffer
+ * @return the size that would need to be written to the byte buffer
+ */
+ public int calculateMarshalSize(T value) {
+ int nativeSize = getNativeSize();
+
+ if (nativeSize == NATIVE_SIZE_DYNAMIC) {
+ throw new AssertionError("Override this function for dynamically-sized objects");
+ }
+
+ return nativeSize;
+ }
+
+ /**
+ * Unmarshal a new object instance from the byte buffer into its managed type.
+ *
+ *
Upon completion, the {@link ByteBuffer#position()} will have advanced by
+ * the {@link #calculateMarshalSize marshal size} of the returned {@code T} instance.
+ *
+ * @param buffer the byte buffer, from which we will read the object
+ * @return a new instance of type T read from the byte buffer
+ */
+ public abstract T unmarshal(ByteBuffer buffer);
+
+ /**
+ * Used to denote variable-length data structures.
+ *
+ *
If the size is dynamic then we can't know ahead of time how big of a data structure
+ * to preallocate for e.g. arrays, so one object must be unmarshaled at a time.
+ */
+ public static int NATIVE_SIZE_DYNAMIC = -1;
+
+ /**
+ * How many bytes a single instance of {@code T} will take up if marshalled to/from
+ * {@code nativeType}.
+ *
+ *
When unmarshaling data from native to managed, the instance {@code T} is not yet
+ * available. If the native size is always a fixed mapping regardless of the instance of
+ * {@code T} (e.g. if the type is not a container of some sort), it can be used to preallocate
+ * containers for {@code T} to avoid resizing them.
+ *
+ *
In particular, the array marshaler takes advantage of this (when size is not dynamic)
+ * to preallocate arrays of the right length when unmarshaling an array {@code T[]}.
+ *
+ * @return a size in bytes, or {@link #NATIVE_SIZE_DYNAMIC} if the size is dynamic
+ */
+ public abstract int getNativeSize();
+
+ /**
+ * The type reference for {@code T} for the managed type side of this marshaler.
+ */
+ public TypeReference getTypeReference() {
+ return mTypeReference;
+ }
+
+ /** The native type corresponding to this marshaler for the native side of this marshaler.*/
+ public int getNativeType() {
+ return mNativeType;
+ }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java
new file mode 100644
index 0000000000000..22b87ef68592f
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java
@@ -0,0 +1,182 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.marshal.MarshalRegistry;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.Log;
+
+import java.lang.reflect.Array;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal any array {@code T}.
+ *
+ *
To marshal any {@code T} to/from a native type, the marshaler for T to/from that native type
+ * also has to exist.
+ *
+ *
{@code T} can be either a T2[] where T2 is an object type, or a P[] where P is a
+ * built-in primitive (e.g. int[], float[], etc).
+
+ * @param the type of the array (e.g. T = int[], or T = Rational[])
+ */
+public class MarshalQueryableArray implements MarshalQueryable {
+
+ private static final String TAG = MarshalQueryableArray.class.getSimpleName();
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ private class MarshalerArray extends Marshaler {
+ private final Class mClass;
+ private final Marshaler> mComponentMarshaler;
+ private final Class> mComponentClass;
+
+ @SuppressWarnings("unchecked")
+ protected MarshalerArray(TypeReference typeReference, int nativeType) {
+ super(MarshalQueryableArray.this, typeReference, nativeType);
+
+ mClass = (Class)typeReference.getRawType();
+
+ TypeReference> componentToken = typeReference.getComponentType();
+ mComponentMarshaler = MarshalRegistry.getMarshaler(componentToken, mNativeType);
+ mComponentClass = componentToken.getRawType();
+ }
+
+ @Override
+ public void marshal(T value, ByteBuffer buffer) {
+ int length = Array.getLength(value);
+ for (int i = 0; i < length; ++i) {
+ marshalArrayElement(mComponentMarshaler, buffer, value, i);
+ }
+ }
+
+ @Override
+ public T unmarshal(ByteBuffer buffer) {
+ Object array;
+
+ int elementSize = mComponentMarshaler.getNativeSize();
+
+ if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) {
+ int remaining = buffer.remaining();
+ int arraySize = remaining / elementSize;
+
+ if (remaining % elementSize != 0) {
+ throw new UnsupportedOperationException("Arrays for " + mTypeReference
+ + " must be packed tighly into a multiple of " + elementSize
+ + "; but there are " + (remaining % elementSize) + " left over bytes");
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, String.format(
+ "Attempting to unpack array (count = %d, element size = %d, bytes "
+ + "remaining = %d) for type %s",
+ arraySize, elementSize, remaining, mClass));
+ }
+
+ array = Array.newInstance(mComponentClass, arraySize);
+ for (int i = 0; i < arraySize; ++i) {
+ Object elem = mComponentMarshaler.unmarshal(buffer);
+ Array.set(array, i, elem);
+ }
+ } else {
+ // Dynamic size, use an array list.
+ ArrayList arrayList = new ArrayList();
+
+ // Assumes array is packed tightly; no unused bytes allowed
+ while (buffer.hasRemaining()) {
+ Object elem = mComponentMarshaler.unmarshal(buffer);
+ arrayList.add(elem);
+ }
+
+ int arraySize = arrayList.size();
+ array = copyListToArray(arrayList, Array.newInstance(mComponentClass, arraySize));
+ }
+
+ if (buffer.remaining() != 0) {
+ Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking "
+ + mClass);
+ }
+
+ return mClass.cast(array);
+ }
+
+ @Override
+ public int getNativeSize() {
+ return NATIVE_SIZE_DYNAMIC;
+ }
+
+ @Override
+ public int calculateMarshalSize(T value) {
+ int elementSize = mComponentMarshaler.getNativeSize();
+ int arrayLength = Array.getLength(value);
+
+ if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) {
+ // The fast way. Every element size is uniform.
+ return elementSize * arrayLength;
+ } else {
+ // The slow way. Accumulate size for each element.
+ int size = 0;
+ for (int i = 0; i < arrayLength; ++i) {
+ size += calculateElementMarshalSize(mComponentMarshaler, value, i);
+ }
+
+ return size;
+ }
+ }
+
+ /*
+ * Helpers to avoid compiler errors regarding types with wildcards (?)
+ */
+
+ @SuppressWarnings("unchecked")
+ private void marshalArrayElement(Marshaler marshaler,
+ ByteBuffer buffer, Object array, int index) {
+ marshaler.marshal((TElem)Array.get(array, index), buffer);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object copyListToArray(ArrayList> arrayList, Object arrayDest) {
+ return arrayList.toArray((T[]) arrayDest);
+ }
+
+ @SuppressWarnings("unchecked")
+ private int calculateElementMarshalSize(Marshaler marshaler,
+ Object array, int index) {
+ Object elem = Array.get(array, index);
+
+ return marshaler.calculateMarshalSize((TElem) elem);
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(TypeReference managedType, int nativeType) {
+ return new MarshalerArray(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference managedType, int nativeType) {
+ // support both ConcreteType[] and GenericType[]
+ return managedType.getRawType().isArray();
+
+ // TODO: Should this recurse deeper and check that there is
+ // a valid marshaler for the ConcreteType as well?
+ }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableBoolean.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableBoolean.java
new file mode 100644
index 0000000000000..4aa4b4ad7b685
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableBoolean.java
@@ -0,0 +1,67 @@
+/*
+ * 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.marshal.impl;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshal booleans: TYPE_BYTE <-> boolean/Boolean
+ */
+public class MarshalQueryableBoolean implements MarshalQueryable {
+
+ private class MarshalerBoolean extends Marshaler {
+ protected MarshalerBoolean(TypeReference typeReference, int nativeType) {
+ super(MarshalQueryableBoolean.this, typeReference, nativeType);
+ }
+
+ @Override
+ public void marshal(Boolean value, ByteBuffer buffer) {
+ boolean unboxValue = value;
+ buffer.put((byte)(unboxValue ? 1 : 0));
+ }
+
+ @Override
+ public Boolean unmarshal(ByteBuffer buffer) {
+ return buffer.get() != 0;
+ }
+
+ @Override
+ public int getNativeSize() {
+ return SIZEOF_BYTE;
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(TypeReference managedType,
+ int nativeType) {
+ return new MarshalerBoolean(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference managedType, int nativeType) {
+ return (Boolean.class.equals(managedType.getType())
+ || boolean.class.equals(managedType.getType())) && nativeType == TYPE_BYTE;
+ }
+
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableColorSpaceTransform.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableColorSpaceTransform.java
new file mode 100644
index 0000000000000..d3796db5969b0
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableColorSpaceTransform.java
@@ -0,0 +1,84 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.ColorSpaceTransform;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal {@link ColorSpaceTransform} to/from {@link #TYPE_RATIONAL}
+ */
+public class MarshalQueryableColorSpaceTransform implements
+ MarshalQueryable {
+
+ private static final int ELEMENTS_INT32 = 3 * 3 * (SIZEOF_RATIONAL / SIZEOF_INT32);
+ private static final int SIZE = SIZEOF_INT32 * ELEMENTS_INT32;
+
+ /** rational x 3 x 3 */
+ private class MarshalerColorSpaceTransform extends Marshaler {
+ protected MarshalerColorSpaceTransform(TypeReference typeReference,
+ int nativeType) {
+ super(MarshalQueryableColorSpaceTransform.this, typeReference, nativeType);
+ }
+
+ @Override
+ public void marshal(ColorSpaceTransform value, ByteBuffer buffer) {
+ int[] transformAsArray = new int[ELEMENTS_INT32];
+ value.copyElements(transformAsArray, /*offset*/0);
+
+ for (int i = 0; i < ELEMENTS_INT32; ++i) {
+ buffer.putInt(transformAsArray[i]);
+ }
+ }
+
+ @Override
+ public ColorSpaceTransform unmarshal(ByteBuffer buffer) {
+ int[] transformAsArray = new int[ELEMENTS_INT32];
+
+ for (int i = 0; i < ELEMENTS_INT32; ++i) {
+ transformAsArray[i] = buffer.getInt();
+ }
+
+ return new ColorSpaceTransform(transformAsArray);
+ }
+
+ @Override
+ public int getNativeSize() {
+ return SIZE;
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(
+ TypeReference managedType, int nativeType) {
+ return new MarshalerColorSpaceTransform(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(
+ TypeReference managedType, int nativeType) {
+ return nativeType == TYPE_RATIONAL &&
+ ColorSpaceTransform.class.equals(managedType.getType());
+ }
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java
new file mode 100644
index 0000000000000..fa53db2aa61f1
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java
@@ -0,0 +1,220 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal any simple enum (0-arg constructors only) into/from either
+ * {@code TYPE_BYTE} or {@code TYPE_INT32}.
+ *
+ *
Default values of the enum are mapped to its ordinal; this can be overridden
+ * by providing a manual value with {@link #registerEnumValues}.
+
+ * @param the type of {@code Enum}
+ */
+public class MarshalQueryableEnum> implements MarshalQueryable {
+
+ private static final String TAG = MarshalQueryableEnum.class.getSimpleName();
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ private static final int UINT8_MIN = 0x0;
+ private static final int UINT8_MAX = (1 << Byte.SIZE) - 1;
+ private static final int UINT8_MASK = UINT8_MAX;
+
+ private class MarshalerEnum extends Marshaler {
+
+ private final Class mClass;
+
+ @SuppressWarnings("unchecked")
+ protected MarshalerEnum(TypeReference typeReference, int nativeType) {
+ super(MarshalQueryableEnum.this, typeReference, nativeType);
+
+ mClass = (Class)typeReference.getRawType();
+ }
+
+ @Override
+ public void marshal(T value, ByteBuffer buffer) {
+ int enumValue = getEnumValue(value);
+
+ if (mNativeType == TYPE_INT32) {
+ buffer.putInt(enumValue);
+ } else if (mNativeType == TYPE_BYTE) {
+ if (enumValue < UINT8_MIN || enumValue > UINT8_MAX) {
+ throw new UnsupportedOperationException(String.format(
+ "Enum value %x too large to fit into unsigned byte", enumValue));
+ }
+ buffer.put((byte)enumValue);
+ } else {
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public T unmarshal(ByteBuffer buffer) {
+ int enumValue;
+
+ switch (mNativeType) {
+ case TYPE_INT32:
+ enumValue = buffer.getInt();
+ break;
+ case TYPE_BYTE:
+ // get the unsigned byte value; avoid sign extension
+ enumValue = buffer.get() & UINT8_MASK;
+ break;
+ default:
+ throw new AssertionError(
+ "Unexpected native type; impossible since its not supported");
+ }
+
+ return getEnumFromValue(mClass, enumValue);
+ }
+
+ @Override
+ public int getNativeSize() {
+ return getPrimitiveTypeSize(mNativeType);
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(TypeReference managedType, int nativeType) {
+ return new MarshalerEnum(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference managedType, int nativeType) {
+ if (nativeType == TYPE_INT32 || nativeType == TYPE_BYTE) {
+ if (managedType.getType() instanceof Class>) {
+ Class> typeClass = (Class>)managedType.getType();
+
+ if (typeClass.isEnum()) {
+ if (VERBOSE) {
+ Log.v(TAG, "possible enum detected for " + typeClass);
+ }
+
+ // The enum must not take extra arguments
+ try {
+ // match a class like: "public enum Fruits { Apple, Orange; }"
+ typeClass.getDeclaredConstructor(String.class, int.class);
+ return true;
+ } catch (NoSuchMethodException e) {
+ // Skip: custom enum with a special constructor e.g. Foo(T), but need Foo()
+ Log.e(TAG, "Can't marshal class " + typeClass + "; no default constructor");
+ } catch (SecurityException e) {
+ // Skip: wouldn't be able to touch the enum anyway
+ Log.e(TAG, "Can't marshal class " + typeClass + "; not accessible");
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static final HashMap, int[]> sEnumValues =
+ new HashMap, int[]>();
+
+ /**
+ * Register a non-sequential set of values to be used with the marshal/unmarshal functions.
+ *
+ *
This enables get/set to correctly marshal the enum into a value that is C-compatible.
+ *
+ * @param enumType The class for an enum
+ * @param values A list of values mapping to the ordinals of the enum
+ */
+ public static > void registerEnumValues(Class enumType, int[] values) {
+ if (enumType.getEnumConstants().length != values.length) {
+ throw new IllegalArgumentException(
+ "Expected values array to be the same size as the enumTypes values "
+ + values.length + " for type " + enumType);
+ }
+ if (VERBOSE) {
+ Log.v(TAG, "Registered enum values for type " + enumType + " values");
+ }
+
+ sEnumValues.put(enumType, values);
+ }
+
+ /**
+ * Get the numeric value from an enum.
+ *
+ *
This is usually the same as the ordinal value for
+ * enums that have fully sequential values, although for C-style enums the range of values
+ * may not map 1:1.
+ *
+ * @param enumValue Enum instance
+ * @return Int guaranteed to be ABI-compatible with the C enum equivalent
+ */
+ private static > int getEnumValue(T enumValue) {
+ int[] values;
+ values = sEnumValues.get(enumValue.getClass());
+
+ int ordinal = enumValue.ordinal();
+ if (values != null) {
+ return values[ordinal];
+ }
+
+ return ordinal;
+ }
+
+ /**
+ * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method.
+ *
+ * @param enumType Class of the enum we want to find
+ * @param value The numeric value of the enum
+ * @return An instance of the enum
+ */
+ private static > T getEnumFromValue(Class enumType, int value) {
+ int ordinal;
+
+ int[] registeredValues = sEnumValues.get(enumType);
+ if (registeredValues != null) {
+ ordinal = -1;
+
+ for (int i = 0; i < registeredValues.length; ++i) {
+ if (registeredValues[i] == value) {
+ ordinal = i;
+ break;
+ }
+ }
+ } else {
+ ordinal = value;
+ }
+
+ T[] values = enumType.getEnumConstants();
+
+ if (ordinal < 0 || ordinal >= values.length) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Argument 'value' (%d) was not a valid enum value for type %s "
+ + "(registered? %b)",
+ value,
+ enumType, (registeredValues != null)));
+ }
+
+ return values[ordinal];
+ }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableMeteringRectangle.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableMeteringRectangle.java
new file mode 100644
index 0000000000000..c8b9bd800239f
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableMeteringRectangle.java
@@ -0,0 +1,88 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.MeteringRectangle;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal {@link MeteringRectangle} to/from {@link #TYPE_INT32}
+ */
+public class MarshalQueryableMeteringRectangle implements MarshalQueryable {
+ private static final int SIZE = SIZEOF_INT32 * 5;
+
+ /** (xmin, ymin, xmax, ymax, weight) */
+ private class MarshalerMeteringRectangle extends Marshaler {
+ protected MarshalerMeteringRectangle(TypeReference typeReference,
+ int nativeType) {
+ super(MarshalQueryableMeteringRectangle.this, typeReference, nativeType);
+ }
+
+ @Override
+ public void marshal(MeteringRectangle value, ByteBuffer buffer) {
+ int xMin = value.getX();
+ int yMin = value.getY();
+ int xMax = xMin + value.getWidth();
+ int yMax = yMin + value.getHeight();
+ int weight = value.getMeteringWeight();
+
+ buffer.putInt(xMin);
+ buffer.putInt(yMin);
+ buffer.putInt(xMax);
+ buffer.putInt(yMax);
+ buffer.putInt(weight);
+ }
+
+ @Override
+ public MeteringRectangle unmarshal(ByteBuffer buffer) {
+ int xMin = buffer.getInt();
+ int yMin = buffer.getInt();
+ int xMax = buffer.getInt();
+ int yMax = buffer.getInt();
+ int weight = buffer.getInt();
+
+ int width = xMax - xMin;
+ int height = yMax - yMin;
+
+ return new MeteringRectangle(xMin, yMin, width, height, weight);
+ }
+
+ @Override
+ public int getNativeSize() {
+ return SIZE;
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(
+ TypeReference managedType, int nativeType) {
+ return new MarshalerMeteringRectangle(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(
+ TypeReference managedType, int nativeType) {
+ return nativeType == TYPE_INT32 && MeteringRectangle.class.equals(managedType.getType());
+ }
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableNativeByteToInteger.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableNativeByteToInteger.java
new file mode 100644
index 0000000000000..3b89c8291d6af
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableNativeByteToInteger.java
@@ -0,0 +1,70 @@
+/*
+ * 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.marshal.impl;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshal fake native enums (ints): TYPE_BYTE <-> int/Integer
+ */
+public class MarshalQueryableNativeByteToInteger implements MarshalQueryable {
+
+ private static final int UINT8_MASK = (1 << Byte.SIZE) - 1;
+
+ private class MarshalerNativeByteToInteger extends Marshaler {
+ protected MarshalerNativeByteToInteger(TypeReference typeReference,
+ int nativeType) {
+ super(MarshalQueryableNativeByteToInteger.this, typeReference, nativeType);
+ }
+
+ @Override
+ public void marshal(Integer value, ByteBuffer buffer) {
+ buffer.put((byte)(int)value); // truncate down to byte
+ }
+
+ @Override
+ public Integer unmarshal(ByteBuffer buffer) {
+ // expand unsigned byte to int; avoid sign extension
+ return buffer.get() & UINT8_MASK;
+ }
+
+ @Override
+ public int getNativeSize() {
+ return SIZEOF_BYTE;
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(TypeReference managedType,
+ int nativeType) {
+ return new MarshalerNativeByteToInteger(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference managedType, int nativeType) {
+ return (Integer.class.equals(managedType.getType())
+ || int.class.equals(managedType.getType())) && nativeType == TYPE_BYTE;
+ }
+
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
new file mode 100644
index 0000000000000..1fd6a1d6237e0
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
@@ -0,0 +1,193 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal any {@code T extends Parcelable} to/from any native type
+ *
+ *
Use with extreme caution! File descriptors and binders will not be marshaled across.
+ */
+public class MarshalQueryableParcelable
+ implements MarshalQueryable {
+
+ private static final String TAG = "MarshalParcelable";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ private static final String FIELD_CREATOR = "CREATOR";
+
+ private class MarshalerParcelable extends Marshaler {
+
+ private final Class mClass;
+ private final Parcelable.Creator mCreator;
+
+ @SuppressWarnings("unchecked")
+ protected MarshalerParcelable(TypeReference typeReference,
+ int nativeType) {
+ super(MarshalQueryableParcelable.this, typeReference, nativeType);
+
+ mClass = (Class)typeReference.getRawType();
+ Field creatorField;
+ try {
+ creatorField = mClass.getDeclaredField(FIELD_CREATOR);
+ } catch (NoSuchFieldException e) {
+ // Impossible. All Parcelable implementations must have a 'CREATOR' static field
+ throw new AssertionError(e);
+ }
+
+ try {
+ mCreator = (Parcelable.Creator)creatorField.get(null);
+ } catch (IllegalAccessException e) {
+ // Impossible: All 'CREATOR' static fields must be public
+ throw new AssertionError(e);
+ } catch (IllegalArgumentException e) {
+ // Impossible: This is a static field, so null must be ok
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public void marshal(T value, ByteBuffer buffer) {
+ if (VERBOSE) {
+ Log.v(TAG, "marshal " + value);
+ }
+
+ Parcel parcel = Parcel.obtain();
+ byte[] parcelContents;
+
+ try {
+ value.writeToParcel(parcel, /*flags*/0);
+
+ if (parcel.hasFileDescriptors()) {
+ throw new UnsupportedOperationException(
+ "Parcelable " + value + " must not have file descriptors");
+ }
+
+ parcelContents = parcel.marshall();
+ }
+ finally {
+ parcel.recycle();
+ }
+
+ if (parcelContents.length == 0) {
+ throw new AssertionError("No data marshaled for " + value);
+ }
+
+ buffer.put(parcelContents);
+ }
+
+ @Override
+ public T unmarshal(ByteBuffer buffer) {
+ if (VERBOSE) {
+ Log.v(TAG, "unmarshal, buffer remaining " + buffer.remaining());
+ }
+
+ /*
+ * Quadratically slow when marshaling an array of parcelables.
+ *
+ * Read out the entire byte buffer as an array, then copy it into the parcel.
+ *
+ * Once we unparcel the entire object, advance the byte buffer by only how many
+ * bytes the parcel actually used up.
+ *
+ * Future: If we ever do need to use parcelable arrays, we can do this a little smarter
+ * by reading out a chunk like 4,8,16,24 each time, but not sure how to detect
+ * parcels being too short in this case.
+ *
+ * Future: Alternatively use Parcel#obtain(long) directly into the native
+ * pointer of a ByteBuffer, which would not copy if the ByteBuffer was direct.
+ */
+ buffer.mark();
+
+ Parcel parcel = Parcel.obtain();
+ try {
+ int maxLength = buffer.remaining();
+
+ byte[] remaining = new byte[maxLength];
+ buffer.get(remaining);
+
+ parcel.unmarshall(remaining, /*offset*/0, maxLength);
+ parcel.setDataPosition(/*pos*/0);
+
+ T value = mCreator.createFromParcel(parcel);
+ int actualLength = parcel.dataPosition();
+
+ if (actualLength == 0) {
+ throw new AssertionError("No data marshaled for " + value);
+ }
+
+ // set the position past the bytes the parcelable actually used
+ buffer.reset();
+ buffer.position(buffer.position() + actualLength);
+
+ if (VERBOSE) {
+ Log.v(TAG, "unmarshal, parcel length was " + actualLength);
+ Log.v(TAG, "unmarshal, value is " + value);
+ }
+
+ return mClass.cast(value);
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ @Override
+ public int getNativeSize() {
+ return NATIVE_SIZE_DYNAMIC;
+ }
+
+ @Override
+ public int calculateMarshalSize(T value) {
+ Parcel parcel = Parcel.obtain();
+ try {
+ value.writeToParcel(parcel, /*flags*/0);
+ int length = parcel.marshall().length;
+
+ if (VERBOSE) {
+ Log.v(TAG, "calculateMarshalSize, length when parceling "
+ + value + " is " + length);
+ }
+
+ return length;
+ } finally {
+ parcel.recycle();
+ }
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(TypeReference managedType, int nativeType) {
+ return new MarshalerParcelable(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference managedType, int nativeType) {
+ return Parcelable.class.isAssignableFrom(managedType.getRawType());
+ }
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePrimitive.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePrimitive.java
new file mode 100644
index 0000000000000..708da7099b530
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePrimitive.java
@@ -0,0 +1,185 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.Rational;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+import static com.android.internal.util.Preconditions.*;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshal/unmarshal built-in primitive types to and from a {@link ByteBuffer}.
+ *
+ *
The following list of type marshaling is supported:
+ *
+ *
byte <-> TYPE_BYTE
+ *
int <-> TYPE_INT32
+ *
long <-> TYPE_INT64
+ *
float <-> TYPE_FLOAT
+ *
double <-> TYPE_DOUBLE
+ *
Rational <-> TYPE_RATIONAL
+ *
+ *
+ *
+ *
Due to the nature of generics, values are always boxed; this also means that both
+ * the boxed and unboxed types are supported (i.e. both {@code int} and {@code Integer}).
+ *
+ *
Each managed type must correspond 1:1 to the native type
+ * (e.g. a byte will not map to a {@link CameraMetadataNative#TYPE_INT32 TYPE_INT32} or vice versa)
+ * for marshaling.
+ */
+public final class MarshalQueryablePrimitive implements MarshalQueryable {
+
+ private class MarshalerPrimitive extends Marshaler {
+ /** Always the wrapped class variant of the primitive class for {@code T} */
+ private final Class mClass;
+
+ @SuppressWarnings("unchecked")
+ protected MarshalerPrimitive(TypeReference typeReference, int nativeType) {
+ super(MarshalQueryablePrimitive.this, typeReference, nativeType);
+
+ // Turn primitives into wrappers, otherwise int.class.cast(Integer) will fail
+ mClass = wrapClassIfPrimitive((Class)typeReference.getRawType());
+ }
+
+ @Override
+ public T unmarshal(ByteBuffer buffer) {
+ return mClass.cast(unmarshalObject(buffer));
+ }
+
+ @Override
+ public int calculateMarshalSize(T value) {
+ return getPrimitiveTypeSize(mNativeType);
+ }
+
+ @Override
+ public void marshal(T value, ByteBuffer buffer) {
+ if (value instanceof Integer) {
+ checkNativeTypeEquals(TYPE_INT32, mNativeType);
+ final int val = (Integer) value;
+ marshalPrimitive(val, buffer);
+ } else if (value instanceof Float) {
+ checkNativeTypeEquals(TYPE_FLOAT, mNativeType);
+ final float val = (Float) value;
+ marshalPrimitive(val, buffer);
+ } else if (value instanceof Long) {
+ checkNativeTypeEquals(TYPE_INT64, mNativeType);
+ final long val = (Long) value;
+ marshalPrimitive(val, buffer);
+ } else if (value instanceof Rational) {
+ checkNativeTypeEquals(TYPE_RATIONAL, mNativeType);
+ marshalPrimitive((Rational) value, buffer);
+ } else if (value instanceof Double) {
+ checkNativeTypeEquals(TYPE_DOUBLE, mNativeType);
+ final double val = (Double) value;
+ marshalPrimitive(val, buffer);
+ } else if (value instanceof Byte) {
+ checkNativeTypeEquals(TYPE_BYTE, mNativeType);
+ final byte val = (Byte) value;
+ marshalPrimitive(val, buffer);
+ } else {
+ throw new UnsupportedOperationException(
+ "Can't marshal managed type " + mTypeReference);
+ }
+ }
+
+ private void marshalPrimitive(int value, ByteBuffer buffer) {
+ buffer.putInt(value);
+ }
+
+ private void marshalPrimitive(float value, ByteBuffer buffer) {
+ buffer.putFloat(value);
+ }
+
+ private void marshalPrimitive(double value, ByteBuffer buffer) {
+ buffer.putDouble(value);
+ }
+
+ private void marshalPrimitive(long value, ByteBuffer buffer) {
+ buffer.putLong(value);
+ }
+
+ private void marshalPrimitive(Rational value, ByteBuffer buffer) {
+ buffer.putInt(value.getNumerator());
+ buffer.putInt(value.getDenominator());
+ }
+
+ private void marshalPrimitive(byte value, ByteBuffer buffer) {
+ buffer.put(value);
+ }
+
+ private Object unmarshalObject(ByteBuffer buffer) {
+ switch (mNativeType) {
+ case TYPE_INT32:
+ return buffer.getInt();
+ case TYPE_FLOAT:
+ return buffer.getFloat();
+ case TYPE_INT64:
+ return buffer.getLong();
+ case TYPE_RATIONAL:
+ int numerator = buffer.getInt();
+ int denominator = buffer.getInt();
+ return new Rational(numerator, denominator);
+ case TYPE_DOUBLE:
+ return buffer.getDouble();
+ case TYPE_BYTE:
+ return buffer.get(); // getByte
+ default:
+ throw new UnsupportedOperationException(
+ "Can't unmarshal native type " + mNativeType);
+ }
+ }
+
+ @Override
+ public int getNativeSize() {
+ return getPrimitiveTypeSize(mNativeType);
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(TypeReference managedType, int nativeType) {
+ return new MarshalerPrimitive(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference managedType, int nativeType) {
+ if (managedType.getType() instanceof Class>) {
+ Class> klass = (Class>)managedType.getType();
+
+ if (klass == byte.class || klass == Byte.class) {
+ return nativeType == TYPE_BYTE;
+ } else if (klass == int.class || klass == Integer.class) {
+ return nativeType == TYPE_INT32;
+ } else if (klass == float.class || klass == Float.class) {
+ return nativeType == TYPE_FLOAT;
+ } else if (klass == long.class || klass == Long.class) {
+ return nativeType == TYPE_INT64;
+ } else if (klass == double.class || klass == Double.class) {
+ return nativeType == TYPE_DOUBLE;
+ } else if (klass == Rational.class) {
+ return nativeType == TYPE_RATIONAL;
+ }
+ }
+ return false;
+ }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRange.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRange.java
new file mode 100644
index 0000000000000..8512804541c58
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRange.java
@@ -0,0 +1,139 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.marshal.MarshalRegistry;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.Range;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal {@link Range} to/from any native type
+ */
+public class MarshalQueryableRange>
+ implements MarshalQueryable> {
+ private static final int RANGE_COUNT = 2;
+
+ private class MarshalerRange extends Marshaler> {
+ private final Class super Range> mClass;
+ private final Constructor> mConstructor;
+ /** Marshal the {@code T} inside of {@code Range} */
+ private final Marshaler mNestedTypeMarshaler;
+
+ @SuppressWarnings("unchecked")
+ protected MarshalerRange(TypeReference> typeReference,
+ int nativeType) {
+ super(MarshalQueryableRange.this, typeReference, nativeType);
+
+ mClass = typeReference.getRawType();
+
+ /*
+ * Lookup the actual type argument, e.g. Range --> Integer
+ * and then get the marshaler for that managed type.
+ */
+ ParameterizedType paramType;
+ try {
+ paramType = (ParameterizedType) typeReference.getType();
+ } catch (ClassCastException e) {
+ throw new AssertionError("Raw use of Range is not supported", e);
+ }
+ Type actualTypeArgument = paramType.getActualTypeArguments()[0];
+
+ TypeReference> actualTypeArgToken =
+ TypeReference.createSpecializedTypeReference(actualTypeArgument);
+
+ mNestedTypeMarshaler = (Marshaler)MarshalRegistry.getMarshaler(
+ actualTypeArgToken, mNativeType);
+ try {
+ mConstructor = (Constructor>)mClass.getConstructor(
+ Comparable.class, Comparable.class);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public void marshal(Range value, ByteBuffer buffer) {
+ mNestedTypeMarshaler.marshal(value.getLower(), buffer);
+ mNestedTypeMarshaler.marshal(value.getUpper(), buffer);
+ }
+
+ @Override
+ public Range unmarshal(ByteBuffer buffer) {
+ T lower = mNestedTypeMarshaler.unmarshal(buffer);
+ T upper = mNestedTypeMarshaler.unmarshal(buffer);
+
+ try {
+ return mConstructor.newInstance(lower, upper);
+ } catch (InstantiationException e) {
+ throw new AssertionError(e);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ } catch (IllegalArgumentException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public int getNativeSize() {
+ int nestedSize = mNestedTypeMarshaler.getNativeSize();
+
+ if (nestedSize != NATIVE_SIZE_DYNAMIC) {
+ return nestedSize * RANGE_COUNT;
+ } else {
+ return NATIVE_SIZE_DYNAMIC;
+ }
+ }
+
+ @Override
+ public int calculateMarshalSize(Range value) {
+ int nativeSize = getNativeSize();
+
+ if (nativeSize != NATIVE_SIZE_DYNAMIC) {
+ return nativeSize;
+ } else {
+ int lowerSize = mNestedTypeMarshaler.calculateMarshalSize(value.getLower());
+ int upperSize = mNestedTypeMarshaler.calculateMarshalSize(value.getUpper());
+
+ return lowerSize + upperSize;
+ }
+ }
+ }
+
+ @Override
+ public Marshaler> createMarshaler(TypeReference> managedType,
+ int nativeType) {
+ return new MarshalerRange(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference> managedType, int nativeType) {
+ return (Range.class.equals(managedType.getRawType()));
+ }
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRect.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRect.java
new file mode 100644
index 0000000000000..de20a1f86cc94
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRect.java
@@ -0,0 +1,77 @@
+/*
+ * 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.marshal.impl;
+
+import android.graphics.Rect;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal {@link Rect} to/from {@link #TYPE_INT32}
+ */
+public class MarshalQueryableRect implements MarshalQueryable {
+ private static final int SIZE = SIZEOF_INT32 * 4;
+
+ private class MarshalerRect extends Marshaler {
+ protected MarshalerRect(TypeReference typeReference,
+ int nativeType) {
+ super(MarshalQueryableRect.this, typeReference, nativeType);
+ }
+
+ @Override
+ public void marshal(Rect value, ByteBuffer buffer) {
+ buffer.putInt(value.left);
+ buffer.putInt(value.top);
+ buffer.putInt(value.width());
+ buffer.putInt(value.height());
+ }
+
+ @Override
+ public Rect unmarshal(ByteBuffer buffer) {
+ int left = buffer.getInt();
+ int top = buffer.getInt();
+ int width = buffer.getInt();
+ int height = buffer.getInt();
+
+ int right = left + width;
+ int bottom = top + height;
+
+ return new Rect(left, top, right, bottom);
+ }
+
+ @Override
+ public int getNativeSize() {
+ return SIZE;
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(TypeReference managedType, int nativeType) {
+ return new MarshalerRect(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference managedType, int nativeType) {
+ return nativeType == TYPE_INT32 && (Rect.class.equals(managedType.getType()));
+ }
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java
new file mode 100644
index 0000000000000..3025cb426e41f
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java
@@ -0,0 +1,129 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.ReprocessFormatsMap;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+
+/**
+ * Marshaler for {@code android.scaler.availableInputOutputFormatsMap} custom class
+ * {@link ReprocessFormatsMap}
+ */
+public class MarshalQueryableReprocessFormatsMap
+ implements MarshalQueryable {
+
+ private class MarshalerReprocessFormatsMap extends Marshaler {
+ protected MarshalerReprocessFormatsMap(
+ TypeReference typeReference, int nativeType) {
+ super(MarshalQueryableReprocessFormatsMap.this, typeReference, nativeType);
+ }
+
+ @Override
+ public void marshal(ReprocessFormatsMap value, ByteBuffer buffer) {
+ /*
+ * // writing (static example, DNG+ZSL)
+ * int32_t[] contents = {
+ * RAW_OPAQUE, 3, RAW16, YUV_420_888, BLOB,
+ * RAW16, 2, YUV_420_888, BLOB,
+ * ...,
+ * INPUT_FORMAT, OUTPUT_FORMAT_COUNT, [OUTPUT_0, OUTPUT_1, ..., OUTPUT_FORMAT_COUNT-1]
+ * };
+ */
+ int[] inputs = value.getInputs();
+ for (int input : inputs) {
+ // INPUT_FORMAT
+ buffer.putInt(input);
+
+ int[] outputs = value.getOutputs(input);
+ // OUTPUT_FORMAT_COUNT
+ buffer.putInt(outputs.length);
+
+ // [OUTPUT_0, OUTPUT_1, ..., OUTPUT_FORMAT_COUNT-1]
+ for (int output : outputs) {
+ buffer.putInt(output);
+ }
+ }
+ }
+
+ @Override
+ public ReprocessFormatsMap unmarshal(ByteBuffer buffer) {
+ int len = buffer.remaining() / SIZEOF_INT32;
+ if (buffer.remaining() % SIZEOF_INT32 != 0) {
+ throw new AssertionError("ReprocessFormatsMap was not TYPE_INT32");
+ }
+
+ int[] entries = new int[len];
+
+ IntBuffer intBuffer = buffer.asIntBuffer();
+ intBuffer.get(entries);
+
+ // TODO: consider moving rest of parsing code from ReprocessFormatsMap to here
+
+ return new ReprocessFormatsMap(entries);
+ }
+
+ @Override
+ public int getNativeSize() {
+ return NATIVE_SIZE_DYNAMIC;
+ }
+
+ @Override
+ public int calculateMarshalSize(ReprocessFormatsMap value) {
+ /*
+ * // writing (static example, DNG+ZSL)
+ * int32_t[] contents = {
+ * RAW_OPAQUE, 3, RAW16, YUV_420_888, BLOB,
+ * RAW16, 2, YUV_420_888, BLOB,
+ * ...,
+ * INPUT_FORMAT, OUTPUT_FORMAT_COUNT, [OUTPUT_0, OUTPUT_1, ..., OUTPUT_FORMAT_COUNT-1]
+ * };
+ */
+ int length = 0;
+
+ int[] inputs = value.getInputs();
+ for (int input : inputs) {
+
+ length += 1; // INPUT_FORMAT
+ length += 1; // OUTPUT_FORMAT_COUNT
+
+ int[] outputs = value.getOutputs(input);
+ length += outputs.length; // [OUTPUT_0, OUTPUT_1, ..., OUTPUT_FORMAT_COUNT-1]
+ }
+
+ return length * SIZEOF_INT32;
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(
+ TypeReference managedType, int nativeType) {
+ return new MarshalerReprocessFormatsMap(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference managedType,
+ int nativeType) {
+ return nativeType == TYPE_INT32 && managedType.getType().equals(ReprocessFormatsMap.class);
+ }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRggbChannelVector.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRggbChannelVector.java
new file mode 100644
index 0000000000000..93c0e925a7fe3
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRggbChannelVector.java
@@ -0,0 +1,75 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.RggbChannelVector;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import java.nio.ByteBuffer;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+/**
+ * Marshal {@link RggbChannelVector} to/from {@link #TYPE_FLOAT} {@code x 4}
+ */
+public class MarshalQueryableRggbChannelVector implements MarshalQueryable {
+ private static final int SIZE = SIZEOF_FLOAT * RggbChannelVector.COUNT;
+
+ private class MarshalerRggbChannelVector extends Marshaler {
+ protected MarshalerRggbChannelVector(TypeReference typeReference,
+ int nativeType) {
+ super(MarshalQueryableRggbChannelVector.this, typeReference, nativeType);
+ }
+
+ @Override
+ public void marshal(RggbChannelVector value, ByteBuffer buffer) {
+ for (int i = 0; i < RggbChannelVector.COUNT; ++i) {
+ buffer.putFloat(value.getComponent(i));
+ }
+ }
+
+ @Override
+ public RggbChannelVector unmarshal(ByteBuffer buffer) {
+ float red = buffer.getFloat();
+ float gEven = buffer.getFloat();
+ float gOdd = buffer.getFloat();
+ float blue = buffer.getFloat();
+
+ return new RggbChannelVector(red, gEven, gOdd, blue);
+ }
+
+ @Override
+ public int getNativeSize() {
+ return SIZE;
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(
+ TypeReference managedType, int nativeType) {
+ return new MarshalerRggbChannelVector(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(
+ TypeReference managedType, int nativeType) {
+ return nativeType == TYPE_FLOAT && (RggbChannelVector.class.equals(managedType.getType()));
+ }
+
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSize.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSize.java
new file mode 100644
index 0000000000000..6a73bee07e4aa
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSize.java
@@ -0,0 +1,68 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.Size;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshal {@link Size} to/from {@code TYPE_INT32}
+ */
+public class MarshalQueryableSize implements MarshalQueryable {
+ private static final int SIZE = SIZEOF_INT32 * 2;
+
+ private class MarshalerSize extends Marshaler {
+ protected MarshalerSize(TypeReference typeReference, int nativeType) {
+ super(MarshalQueryableSize.this, typeReference, nativeType);
+ }
+
+ @Override
+ public void marshal(Size value, ByteBuffer buffer) {
+ buffer.putInt(value.getWidth());
+ buffer.putInt(value.getHeight());
+ }
+
+ @Override
+ public Size unmarshal(ByteBuffer buffer) {
+ int width = buffer.getInt();
+ int height = buffer.getInt();
+
+ return new Size(width, height);
+ }
+
+ @Override
+ public int getNativeSize() {
+ return SIZE;
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(TypeReference managedType, int nativeType) {
+ return new MarshalerSize(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference managedType, int nativeType) {
+ return nativeType == TYPE_INT32 && (Size.class.equals(managedType.getType()));
+ }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSizeF.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSizeF.java
new file mode 100644
index 0000000000000..b60a46dc3f6ff
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableSizeF.java
@@ -0,0 +1,72 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.SizeF;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshal {@link SizeF} to/from {@code TYPE_FLOAT}
+ */
+public class MarshalQueryableSizeF implements MarshalQueryable {
+
+ private static final int SIZE = SIZEOF_FLOAT * 2;
+
+ private class MarshalerSizeF extends Marshaler {
+
+ protected MarshalerSizeF(TypeReference typeReference, int nativeType) {
+ super(MarshalQueryableSizeF.this, typeReference, nativeType);
+ }
+
+ @Override
+ public void marshal(SizeF value, ByteBuffer buffer) {
+ buffer.putFloat(value.getWidth());
+ buffer.putFloat(value.getHeight());
+ }
+
+ @Override
+ public SizeF unmarshal(ByteBuffer buffer) {
+ float width = buffer.getFloat();
+ float height = buffer.getFloat();
+
+ return new SizeF(width, height);
+ }
+
+ @Override
+ public int getNativeSize() {
+ return SIZE;
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(
+ TypeReference managedType, int nativeType) {
+ return new MarshalerSizeF(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference managedType, int nativeType) {
+ return nativeType == TYPE_FLOAT && (SizeF.class.equals(managedType.getType()));
+ }
+}
+
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java
new file mode 100644
index 0000000000000..6a4e82180b8ef
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java
@@ -0,0 +1,80 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.StreamConfiguration;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshaler for {@code android.scaler.availableStreamConfigurations} custom class
+ * {@link StreamConfiguration}
+ *
+ *
Data is stored as {@code (format, width, height, input?)} tuples (int32).
+ */
+public class MarshalQueryableStreamConfiguration
+ implements MarshalQueryable {
+ private static final int SIZE = SIZEOF_INT32 * 4;
+
+ private class MarshalerStreamConfiguration extends Marshaler {
+ protected MarshalerStreamConfiguration(TypeReference typeReference,
+ int nativeType) {
+ super(MarshalQueryableStreamConfiguration.this, typeReference, nativeType);
+ }
+
+ @Override
+ public void marshal(StreamConfiguration value, ByteBuffer buffer) {
+ buffer.putInt(value.getFormat());
+ buffer.putInt(value.getWidth());
+ buffer.putInt(value.getHeight());
+ buffer.putInt(value.isInput() ? 1 : 0);
+ }
+
+ @Override
+ public StreamConfiguration unmarshal(ByteBuffer buffer) {
+ int format = buffer.getInt();
+ int width = buffer.getInt();
+ int height = buffer.getInt();
+ boolean input = buffer.getInt() != 0;
+
+ return new StreamConfiguration(format, width, height, input);
+ }
+
+ @Override
+ public int getNativeSize() {
+ return SIZE;
+ }
+
+ }
+
+ @Override
+ public Marshaler createMarshaler(
+ TypeReference managedType, int nativeType) {
+ return new MarshalerStreamConfiguration(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference managedType,
+ int nativeType) {
+ return nativeType == TYPE_INT32 && managedType.getType().equals(StreamConfiguration.class);
+ }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java
new file mode 100644
index 0000000000000..c3d564e1d6b10
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java
@@ -0,0 +1,90 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.StreamConfigurationDuration;
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshaler for custom class {@link StreamConfigurationDuration} for min-frame and stall durations.
+ *
+ *
+ * Data is stored as {@code (format, width, height, durationNs)} tuples (int64).
+ *
+ */
+public class MarshalQueryableStreamConfigurationDuration
+ implements MarshalQueryable {
+
+ private static final int SIZE = SIZEOF_INT64 * 4;
+ /**
+ * Values and-ed with this will do an unsigned int to signed long conversion;
+ * in other words the sign bit from the int will not be extended.
+ * */
+ private static final long MASK_UNSIGNED_INT = 0x00000000ffffffffL;
+
+ private class MarshalerStreamConfigurationDuration
+ extends Marshaler {
+
+ protected MarshalerStreamConfigurationDuration(
+ TypeReference typeReference, int nativeType) {
+ super(MarshalQueryableStreamConfigurationDuration.this, typeReference, nativeType);
+ }
+
+ @Override
+ public void marshal(StreamConfigurationDuration value, ByteBuffer buffer) {
+ buffer.putLong(value.getFormat() & MASK_UNSIGNED_INT); // unsigned int -> long
+ buffer.putLong(value.getWidth());
+ buffer.putLong(value.getHeight());
+ buffer.putLong(value.getDuration());
+ }
+
+ @Override
+ public StreamConfigurationDuration unmarshal(ByteBuffer buffer) {
+ int format = (int)buffer.getLong();
+ int width = (int)buffer.getLong();
+ int height = (int)buffer.getLong();
+ long durationNs = buffer.getLong();
+
+ return new StreamConfigurationDuration(format, width, height, durationNs);
+ }
+
+ @Override
+ public int getNativeSize() {
+ return SIZE;
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(
+ TypeReference managedType, int nativeType) {
+ return new MarshalerStreamConfigurationDuration(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference managedType,
+ int nativeType) {
+ return nativeType == TYPE_INT64 &&
+ (StreamConfigurationDuration.class.equals(managedType.getType()));
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableString.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableString.java
new file mode 100644
index 0000000000000..bf518bb4e7900
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableString.java
@@ -0,0 +1,110 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+
+/**
+ * Marshal {@link String} to/from {@link #TYPE_BYTE}.
+ */
+public class MarshalQueryableString implements MarshalQueryable {
+
+ private static final String TAG = MarshalQueryableString.class.getSimpleName();
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+ private static final byte NUL = (byte)'\0'; // used as string terminator
+
+ private class MarshalerString extends Marshaler {
+
+ protected MarshalerString(TypeReference typeReference, int nativeType) {
+ super(MarshalQueryableString.this, typeReference, nativeType);
+ }
+
+ @Override
+ public void marshal(String value, ByteBuffer buffer) {
+ byte[] arr = value.getBytes(UTF8_CHARSET);
+
+ buffer.put(arr);
+ buffer.put(NUL); // metadata strings are NUL-terminated
+ }
+
+ @Override
+ public int calculateMarshalSize(String value) {
+ byte[] arr = value.getBytes(UTF8_CHARSET);
+
+ return arr.length + 1; // metadata strings are NUL-terminated
+ }
+
+ @Override
+ public String unmarshal(ByteBuffer buffer) {
+ buffer.mark(); // save the current position
+
+ boolean foundNull = false;
+ int stringLength = 0;
+ while (buffer.hasRemaining()) {
+ if (buffer.get() == NUL) {
+ foundNull = true;
+ break;
+ }
+
+ stringLength++;
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG,
+ "unmarshal - scanned " + stringLength + " characters; found null? "
+ + foundNull);
+ }
+
+ if (!foundNull) {
+ throw new UnsupportedOperationException("Strings must be null-terminated");
+ }
+
+ buffer.reset(); // go back to the previously marked position
+
+ byte[] strBytes = new byte[stringLength + 1];
+ buffer.get(strBytes, /*dstOffset*/0, stringLength + 1); // including null character
+
+ // not including null character
+ return new String(strBytes, /*offset*/0, stringLength, UTF8_CHARSET);
+ }
+
+ @Override
+ public int getNativeSize() {
+ return NATIVE_SIZE_DYNAMIC;
+ }
+ }
+
+ @Override
+ public Marshaler createMarshaler(
+ TypeReference managedType, int nativeType) {
+ return new MarshalerString(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference managedType, int nativeType) {
+ return nativeType == TYPE_BYTE && String.class.equals(managedType.getType());
+ }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/package.html b/core/java/android/hardware/camera2/marshal/impl/package.html
new file mode 100644
index 0000000000000..783d0a1b54d52
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/package.html
@@ -0,0 +1,3 @@
+
+{@hide}
+
diff --git a/core/java/android/hardware/camera2/marshal/package.html b/core/java/android/hardware/camera2/marshal/package.html
new file mode 100644
index 0000000000000..783d0a1b54d52
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/package.html
@@ -0,0 +1,3 @@
+
+{@hide}
+
diff --git a/core/java/android/hardware/camera2/impl/HashCodeHelpers.java b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java
similarity index 99%
rename from core/java/android/hardware/camera2/impl/HashCodeHelpers.java
rename to core/java/android/hardware/camera2/utils/HashCodeHelpers.java
index 2d63827b1ce7e..b980549db99c3 100644
--- a/core/java/android/hardware/camera2/impl/HashCodeHelpers.java
+++ b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.camera2.impl;
+package android.hardware.camera2.utils;
/**
* Provide hashing functions using the Modified Bernstein hash
diff --git a/core/java/android/hardware/camera2/utils/TypeReference.java b/core/java/android/hardware/camera2/utils/TypeReference.java
new file mode 100644
index 0000000000000..d0c919c7831bf
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/TypeReference.java
@@ -0,0 +1,435 @@
+/*
+ * 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.utils;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Super type token; allows capturing generic types at runtime by forcing them to be reified.
+ *
+ *
Usage example:
{@code
+ * // using anonymous classes (preferred)
+ * TypeReference<Integer> intToken = new TypeReference<Integer>() {{ }};
+ *
+ * // using named classes
+ * class IntTypeReference extends TypeReference<Integer> {...}
+ * TypeReference<Integer> intToken = new IntTypeReference();
+ * }
+ *
+ *
Unlike the reference implementation, this bans nested TypeVariables; that is all
+ * dynamic types must equal to the static types.
+ */
+public abstract class TypeReference {
+ private final Type mType;
+
+ /**
+ * Create a new type reference for {@code T}.
+ *
+ * @throws IllegalArgumentException if {@code T}'s actual type contains a type variable
+ *
+ * @see TypeReference
+ */
+ protected TypeReference() {
+ ParameterizedType thisType = (ParameterizedType)getClass().getGenericSuperclass();
+
+ // extract the "T" from TypeReference
+ mType = thisType.getActualTypeArguments()[0];
+
+ /*
+ * Prohibit type references with type variables such as
+ *
+ * class GenericListToken extends TypeReference>
+ *
+ * Since the "T" there is not known without an instance of T, type equality would
+ * consider *all* Lists equal regardless of T. Allowing this would defeat
+ * some of the type safety of a type reference.
+ */
+ if (containsTypeVariable(mType)) {
+ throw new IllegalArgumentException(
+ "Including a type variable in a type reference is not allowed");
+ }
+ }
+
+ /**
+ * Return the dynamic {@link Type} corresponding to the captured type {@code T}.
+ */
+ public Type getType() {
+ return mType;
+ }
+
+ private TypeReference(Type type) {
+ mType = type;
+
+ if (containsTypeVariable(mType)) {
+ throw new IllegalArgumentException(
+ "Including a type variable in a type reference is not allowed");
+ }
+ }
+
+ private static class SpecializedTypeReference extends TypeReference {
+ public SpecializedTypeReference(Class klass) {
+ super(klass);
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static class SpecializedBaseTypeReference extends TypeReference {
+ public SpecializedBaseTypeReference(Type type) {
+ super(type);
+ }
+ }
+
+ /**
+ * Create a specialized type reference from a dynamic class instance,
+ * bypassing the standard compile-time checks.
+ *
+ *
As with a regular type reference, the {@code klass} must not contain
+ * any type variables.
+ *
+ * @param klass a non-{@code null} {@link Class} instance
+ *
+ * @return a type reference which captures {@code T} at runtime
+ *
+ * @throws IllegalArgumentException if {@code T} had any type variables
+ */
+ public static TypeReference createSpecializedTypeReference(Class klass) {
+ return new SpecializedTypeReference(klass);
+ }
+
+ /**
+ * Create a specialized type reference from a dynamic {@link Type} instance,
+ * bypassing the standard compile-time checks.
+ *
+ *
As with a regular type reference, the {@code type} must not contain
+ * any type variables.
+ *
+ * @param type a non-{@code null} {@link Type} instance
+ *
+ * @return a type reference which captures {@code T} at runtime
+ *
+ * @throws IllegalArgumentException if {@code type} had any type variables
+ */
+ public static TypeReference> createSpecializedTypeReference(Type type) {
+ return new SpecializedBaseTypeReference(type);
+ }
+
+ /**
+ * Returns the raw type of T.
+ *
+ *
+ *
If T is a Class itself, T itself is returned.
+ *
If T is a ParameterizedType, the raw type of the parameterized type is returned.
+ *
If T is a GenericArrayType, the returned type is the corresponding array class.
+ * For example: {@code List[]} => {@code List[]}.
+ *
If T is a type variable or a wildcard type, the raw type of the first upper bound is
+ * returned. For example: {@code } => {@code Foo}.
+ *
+ *
+ * @return the raw type of {@code T}
+ */
+ @SuppressWarnings("unchecked")
+ public final Class super T> getRawType() {
+ return (Class super T>)getRawType(mType);
+ }
+
+ private static final Class> getRawType(Type type) {
+ if (type == null) {
+ throw new NullPointerException("type must not be null");
+ }
+
+ if (type instanceof Class>) {
+ return (Class>)type;
+ } else if (type instanceof ParameterizedType) {
+ return (Class>)(((ParameterizedType)type).getRawType());
+ } else if (type instanceof GenericArrayType) {
+ return getArrayClass(getRawType(((GenericArrayType)type).getGenericComponentType()));
+ } else if (type instanceof WildcardType) {
+ // Should be at most 1 upper bound, but treat it like an array for simplicity
+ return getRawType(((WildcardType) type).getUpperBounds());
+ } else if (type instanceof TypeVariable) {
+ throw new AssertionError("Type variables are not allowed in type references");
+ } else {
+ // Impossible
+ throw new AssertionError("Unhandled branch to get raw type for type " + type);
+ }
+ }
+
+ private static final Class> getRawType(Type[] types) {
+ if (types == null) {
+ return null;
+ }
+
+ for (Type type : types) {
+ Class> klass = getRawType(type);
+ if (klass != null) {
+ return klass;
+ }
+ }
+
+ return null;
+ }
+
+ private static final Class> getArrayClass(Class> componentType) {
+ return Array.newInstance(componentType, 0).getClass();
+ }
+
+ /**
+ * Get the component type, e.g. {@code T} from {@code T[]}.
+ *
+ * @return component type, or {@code null} if {@code T} is not an array
+ */
+ public TypeReference> getComponentType() {
+ Type componentType = getComponentType(mType);
+
+ return (componentType != null) ?
+ createSpecializedTypeReference(componentType) :
+ null;
+ }
+
+ private static Type getComponentType(Type type) {
+ checkNotNull(type, "type must not be null");
+
+ if (type instanceof Class>) {
+ return ((Class>) type).getComponentType();
+ } else if (type instanceof ParameterizedType) {
+ return null;
+ } else if (type instanceof GenericArrayType) {
+ return ((GenericArrayType)type).getGenericComponentType();
+ } else if (type instanceof WildcardType) {
+ // Should be at most 1 upper bound, but treat it like an array for simplicity
+ throw new UnsupportedOperationException("TODO: support wild card components");
+ } else if (type instanceof TypeVariable) {
+ throw new AssertionError("Type variables are not allowed in type references");
+ } else {
+ // Impossible
+ throw new AssertionError("Unhandled branch to get component type for type " + type);
+ }
+ }
+
+ /**
+ * Compare two objects for equality.
+ *
+ *
A TypeReference is only equal to another TypeReference if their captured type {@code T}
+ * is also equal.
+ */
+ @Override
+ public boolean equals(Object o) {
+ // Note that this comparison could inaccurately return true when comparing types
+ // with nested type variables; therefore we ban type variables in the constructor.
+ return o instanceof TypeReference> && mType.equals(((TypeReference>)o).mType);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return mType.hashCode();
+ }
+
+ /**
+ * Check if the {@code type} contains a {@link TypeVariable} recursively.
+ *
+ *
Intuitively, a type variable is a type in a type expression that refers to a generic
+ * type which is not known at the definition of the expression (commonly seen when
+ * type parameters are used, e.g. {@code class Foo}).
.
+ *
+ * @param type a type object ({@code null} is allowed)
+ * @return {@code true} if there were nested type variables; {@code false} otherwise
+ */
+ public static boolean containsTypeVariable(Type type) {
+ if (type == null) {
+ // Trivially false
+ return false;
+ } else if (type instanceof TypeVariable>) {
+ /*
+ * T -> trivially true
+ */
+ return true;
+ } else if (type instanceof Class>) {
+ /*
+ * class Foo -> no type variable
+ * class Foo - has a type variable
+ *
+ * This also covers the case of class Foo extends ... / implements ...
+ * since everything on the right hand side would either include a type variable T
+ * or have no type variables.
+ */
+ Class> klass = (Class>)type;
+
+ // Empty array => class is not generic
+ if (klass.getTypeParameters().length != 0) {
+ return true;
+ } else {
+ // Does the outer class(es) contain any type variables?
+
+ /*
+ * class Outer {
+ * class Inner {
+ * T field;
+ * }
+ * }
+ *
+ * In this case 'Inner' has no type parameters itself, but it still has a type
+ * variable as part of the type definition.
+ */
+ return containsTypeVariable(klass.getDeclaringClass());
+ }
+ } else if (type instanceof ParameterizedType) {
+ /*
+ * This is the "Foo" in the scope of a
+ *
+ * // no type variables here, T1-Tn are known at this definition
+ * class X extends Foo
+ *
+ * // T1 is a type variable, T2-Tn are known at this definition
+ * class X extends Foo
+ */
+ ParameterizedType p = (ParameterizedType) type;
+
+ // This needs to be recursively checked
+ for (Type arg : p.getActualTypeArguments()) {
+ if (containsTypeVariable(arg)) {
+ return true;
+ }
+ }
+
+ return false;
+ } else if (type instanceof WildcardType) {
+ WildcardType wild = (WildcardType) type;
+
+ /*
+ * This is is the "?" inside of a
+ *
+ * Foo> --> unbounded; trivially no type variables
+ * Foo super T> --> lower bound; does T have a type variable?
+ * Foo extends T> --> upper bound; does T have a type variable?
+ */
+
+ /*
+ * According to JLS 4.5.1
+ * (http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.5.1):
+ *
+ * - More than 1 lower/upper bound is illegal
+ * - Both a lower and upper bound is illegal
+ *
+ * However, we use this 'array OR array' approach for readability
+ */
+ return containsTypeVariable(wild.getLowerBounds()) ||
+ containsTypeVariable(wild.getUpperBounds());
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("TypeReference<");
+ toString(getType(), builder);
+ builder.append(">");
+
+ return builder.toString();
+ }
+
+ private static void toString(Type type, StringBuilder out) {
+ if (type == null) {
+ return;
+ } else if (type instanceof TypeVariable>) {
+ // T
+ out.append(((TypeVariable>)type).getName());
+ } else if (type instanceof Class>) {
+ Class> klass = (Class>)type;
+
+ out.append(klass.getName());
+ toString(klass.getTypeParameters(), out);
+ } else if (type instanceof ParameterizedType) {
+ // "Foo"
+ ParameterizedType p = (ParameterizedType) type;
+
+ out.append(((Class>)p.getRawType()).getName());
+ toString(p.getActualTypeArguments(), out);
+ } else if (type instanceof GenericArrayType) {
+ GenericArrayType gat = (GenericArrayType)type;
+
+ toString(gat.getGenericComponentType(), out);
+ out.append("[]");
+ } else { // WildcardType, BoundedType
+ // TODO:
+ out.append(type.toString());
+ }
+ }
+
+ private static void toString(Type[] types, StringBuilder out) {
+ if (types == null) {
+ return;
+ } else if (types.length == 0) {
+ return;
+ }
+
+ out.append("<");
+
+ for (int i = 0; i < types.length; ++i) {
+ toString(types[i], out);
+ if (i != types.length - 1) {
+ out.append(", ");
+ }
+ }
+
+ out.append(">");
+ }
+
+ /**
+ * Check if any of the elements in this array contained a type variable.
+ *
+ *
Empty and null arrays trivially have no type variables.
+ *
+ * @param typeArray an array ({@code null} is ok) of types
+ * @return true if any elements contained a type variable; false otherwise
+ */
+ private static boolean containsTypeVariable(Type[] typeArray) {
+ if (typeArray == null) {
+ return false;
+ }
+
+ for (Type type : typeArray) {
+ if (containsTypeVariable(type)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java
index 9a4bd4b3f2053..d7e8cf038929e 100644
--- a/core/java/android/util/Range.java
+++ b/core/java/android/util/Range.java
@@ -18,7 +18,7 @@ package android.util;
import static com.android.internal.util.Preconditions.*;
-import android.hardware.camera2.impl.HashCodeHelpers;
+import android.hardware.camera2.utils.HashCodeHelpers;
/**
* Immutable class for describing the range of two numeric values.
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 05a99a36a5602..fa2cfe3d350d6 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -489,8 +489,13 @@ static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyN
sp vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor();
- SortedVector vendorSections = vTags->getAllSectionNames();
- size_t vendorSectionCount = vendorSections.size();
+ SortedVector vendorSections;
+ size_t vendorSectionCount = 0;
+
+ if (vTags != 0) {
+ vendorSections = vTags->getAllSectionNames();
+ vendorSectionCount = vendorSections.size();
+ }
// First, find the section by the longest string match
const char *section = NULL;
@@ -561,7 +566,7 @@ static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyN
"Could not find tag name for key '%s')", key);
return 0;
}
- } else {
+ } else if (vTags != 0) {
// Match vendor tags (typically com.*)
const String8 sectionName(section);
const String8 tagName(keyTagName);
diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk
index 1e6b2e77029e4..42da48d0969b1 100644
--- a/media/tests/MediaFrameworkTest/Android.mk
+++ b/media/tests/MediaFrameworkTest/Android.mk
@@ -7,7 +7,7 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := easymocklib mockito-target
+LOCAL_STATIC_JAVA_LIBRARIES := easymocklib mockito-target core-tests android-support-test
LOCAL_PACKAGE_NAME := mediaframeworktest
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index 91ee2c60cef83..c62199f1ceb3c 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -76,4 +76,8 @@
android:label="MediaFramework integration tests InstrumentationRunner">
+
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
index 64b12b7648ce0..11d90700bffde 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
@@ -63,6 +63,7 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner {
suite.addTestSuite(CameraUtilsRuntimeExceptionTest.class);
suite.addTestSuite(CameraUtilsUncheckedThrowTest.class);
suite.addTestSuite(CameraUtilsBinderDecoratorTest.class);
+ suite.addTestSuite(CameraUtilsTypeReferenceTest.class);
suite.addTestSuite(CameraMetadataTest.class);
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java
new file mode 100644
index 0000000000000..6f314808dc2f8
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java
@@ -0,0 +1,253 @@
+/*
+ * 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 com.android.mediaframeworktest.unit;
+
+import android.util.Log;
+
+import java.lang.reflect.Array;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+public class ByteArrayHelpers {
+
+ private static final String TAG = "ByteArrayHelpers";
+ private static boolean VERBOSE = false;
+
+ /**
+ * Convert an array of byte primitives to a {@code byte[]} using native endian order.
+ *
+ *
This function is a pass-through; it's here only to provide overloads for
+ * every single type of primitive arrays.
+ *
+ * @param array array of primitives
+ * @return array
+ */
+ public static byte[] toByteArray(byte[] array) {
+ return array;
+ }
+
+ /**
+ * Convert an array of shorts to a {@code byte[]} using native endian order.
+ *
+ * @param array array of shorts
+ * @return array converted into byte array using native endian order
+ */
+ public static byte[] toByteArray(short[] array) {
+ return toByteArray(array, Short.SIZE);
+ }
+
+ /**
+ * Convert an array of chars to a {@code byte[]} using native endian order.
+ *
+ * @param array array of chars
+ * @return array converted into byte array using native endian order
+ */
+ public static byte[] toByteArray(char[] array) {
+ return toByteArray(array, Character.SIZE);
+ }
+ /**
+ * Convert an array of ints to a {@code byte[]} using native endian order.
+ *
+ * @param array array of ints
+ * @return array converted into byte array using native endian order
+ */
+ public static byte[] toByteArray(int[] array) {
+ return toByteArray(array, Integer.SIZE);
+ }
+ /**
+ * Convert an array of longs to a {@code byte[]} using native endian order.
+ *
+ * @param array array of longs
+ * @return array converted into byte array using native endian order
+ */
+ public static byte[] toByteArray(long[] array) {
+ return toByteArray(array, Long.SIZE);
+ }
+ /**
+ * Convert an array of floats to a {@code byte[]} using native endian order.
+ *
+ * @param array array of floats
+ * @return array converted into byte array using native endian order
+ */
+ public static byte[] toByteArray(float[] array) {
+ return toByteArray(array, Float.SIZE);
+ }
+ /**
+ * Convert an array of doubles to a {@code byte[]} using native endian order.
+ *
+ * @param array array of doubles
+ * @return array converted into byte array using native endian order
+ */
+ public static byte[] toByteArray(double[] array) {
+ return toByteArray(array, Double.SIZE);
+ }
+
+ /**
+ * Convert an array of primitives to a {@code byte[]} using native endian order.
+ *
+ *
Arguments other than arrays are not supported. The array component must be primitive,
+ * the wrapper class is not allowed (e.g. {@code int[]} is ok, but {@code Integer[]} is not.
+ *
+ * @param array array of primitives
+ * @return array converted into byte array using native endian order
+ *
+ * @throws IllegalArgumentException if {@code array} was not an array of primitives
+ */
+ public static byte[] toByteArray(T array) {
+ @SuppressWarnings("unchecked")
+ Class klass = (Class) array.getClass();
+
+ if (!klass.isArray()) {
+ throw new IllegalArgumentException("array class must be an array");
+ }
+
+ Class> componentClass = klass.getComponentType();
+
+ if (!componentClass.isPrimitive()) {
+ throw new IllegalArgumentException("array's component must be a primitive");
+ }
+
+ int sizeInBits;
+ if (klass == int.class) {
+ sizeInBits = Integer.SIZE;
+ } else if (klass == float.class) {
+ sizeInBits = Float.SIZE;
+ } else if (klass == double.class) {
+ sizeInBits = Double.SIZE;
+ } else if (klass == short.class) {
+ sizeInBits = Short.SIZE;
+ } else if (klass == char.class) {
+ sizeInBits = Character.SIZE;
+ } else if (klass == long.class) {
+ sizeInBits = Long.SIZE;
+ } else if (klass == byte.class) {
+ sizeInBits = Byte.SIZE;
+ } else {
+ throw new AssertionError();
+ }
+
+ return toByteArray(array, sizeInBits);
+ }
+
+ /**
+ * Convert a variadic list of {@code Number}s into a byte array using native endian order.
+ *
+ *
Each {@link Number} must be an instance of a primitive wrapper class
+ * (e.g. {@link Integer} is OK, since it wraps {@code int}, but {@code BigInteger} is not.
+ *
+ * @param numbers variadic list of numeric values
+ * @return array converted into byte array using native endian order
+ *
+ * @throws IllegalArgumentException
+ * if {@code numbers} contained a class that wasn't a primitive wrapper
+ */
+ public static byte[] toByteArray(Number... numbers) {
+ if (numbers.length == 0) {
+ throw new IllegalArgumentException("too few numbers");
+ }
+
+ if (VERBOSE) Log.v(TAG, "toByteArray - input: " + Arrays.toString(numbers));
+
+ // Have a large enough capacity to fit in every number as a double
+ ByteBuffer byteBuffer = ByteBuffer.allocate(numbers.length * (Double.SIZE / Byte.SIZE))
+ .order(ByteOrder.nativeOrder());
+
+ for (int i = 0; i < numbers.length; ++i) {
+ Number value = numbers[i];
+ Class extends Number> klass = value.getClass();
+
+ if (VERBOSE) Log.v(TAG, "toByteArray - number " + i + ", class " + klass);
+
+ if (klass == Integer.class) {
+ byteBuffer.putInt((Integer)value);
+ } else if (klass == Float.class) {
+ byteBuffer.putFloat((Float)value);
+ } else if (klass == Double.class) {
+ byteBuffer.putDouble((Double)value);
+ } else if (klass == Short.class) {
+ byteBuffer.putShort((Short)value);
+ } else if (klass == Long.class) {
+ byteBuffer.putLong((Long)value);
+ } else if (klass == Byte.class) {
+ byteBuffer.put((Byte)value);
+ } else {
+ throw new IllegalArgumentException(
+ "number class invalid; must be wrapper around primitive class");
+ }
+ }
+
+ if (VERBOSE) Log.v(TAG, "toByteArray - end of loop");
+
+ // Each number written is at least 1 byte, so the position should be at least length
+ if (numbers.length != 0 && byteBuffer.position() < numbers.length) {
+ throw new AssertionError(String.format(
+ "Had %d numbers, but byte buffer position was only %d",
+ numbers.length, byteBuffer.position()));
+ }
+
+ byteBuffer.flip();
+
+ byte[] bytes = new byte[byteBuffer.limit()];
+ byteBuffer.get(bytes);
+
+ if (VERBOSE) Log.v(TAG, "toByteArray - output: " + Arrays.toString(bytes));
+
+ return bytes;
+ }
+
+ private static byte[] toByteArray(T array, int sizeOfTBits) {
+ @SuppressWarnings("unchecked")
+ Class klass = (Class) array.getClass();
+
+ if (!klass.isArray()) {
+ throw new IllegalArgumentException("array class must be an array");
+ }
+
+ int sizeOfT = sizeOfTBits / Byte.SIZE;
+ int byteLength = Array.getLength(array) * sizeOfT;
+
+ if (klass == byte[].class) {
+ // Always return a copy
+ return Arrays.copyOf((byte[])array, byteLength);
+ }
+
+ ByteBuffer byteBuffer = ByteBuffer.allocate(byteLength).order(ByteOrder.nativeOrder());
+
+ if (klass == int[].class) {
+ byteBuffer.asIntBuffer().put((int[])array);
+ } else if (klass == float[].class) {
+ byteBuffer.asFloatBuffer().put((float[])array);
+ } else if (klass == double[].class) {
+ byteBuffer.asDoubleBuffer().put((double[])array);
+ } else if (klass == short[].class) {
+ byteBuffer.asShortBuffer().put((short[])array);
+ } else if (klass == char[].class) {
+ byteBuffer.asCharBuffer().put((char[])array);
+ } else if (klass == long[].class) {
+ byteBuffer.asLongBuffer().put((long[])array);
+ } else {
+ throw new IllegalArgumentException("array class invalid; must be a primitive array");
+ }
+
+ return byteBuffer.array();
+ }
+
+ private ByteArrayHelpers() {
+ throw new AssertionError();
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index 45df065eb30a7..b28733ab431ee 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -16,18 +16,30 @@
package com.android.mediaframeworktest.unit;
-import android.os.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Range;
+import android.util.SizeF;
+import android.graphics.ImageFormat;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.ColorSpaceTransform;
import android.hardware.camera2.Face;
+import android.hardware.camera2.MeteringRectangle;
import android.hardware.camera2.Rational;
+import android.hardware.camera2.ReprocessFormatsMap;
+import android.hardware.camera2.RggbChannelVector;
import android.hardware.camera2.Size;
+import android.hardware.camera2.StreamConfiguration;
+import android.hardware.camera2.StreamConfigurationDuration;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
+import android.hardware.camera2.utils.TypeReference;
import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static com.android.mediaframeworktest.unit.ByteArrayHelpers.*;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
@@ -43,7 +55,6 @@ import java.nio.ByteOrder;
public class CameraMetadataTest extends junit.framework.TestCase {
CameraMetadataNative mMetadata;
- Parcel mParcel;
// Sections
static final int ANDROID_COLOR_CORRECTION = 0;
@@ -64,15 +75,11 @@ public class CameraMetadataTest extends junit.framework.TestCase {
@Override
public void setUp() {
mMetadata = new CameraMetadataNative();
- mParcel = Parcel.obtain();
}
@Override
public void tearDown() throws Exception {
mMetadata = null;
-
- mParcel.recycle();
- mParcel = null;
}
@SmallTest
@@ -130,10 +137,14 @@ public class CameraMetadataTest extends junit.framework.TestCase {
@SmallTest
public void testGetTypeFromTag() {
- assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
- assertEquals(TYPE_RATIONAL, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
- assertEquals(TYPE_FLOAT, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS));
- assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
+ assertEquals(TYPE_BYTE,
+ CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
+ assertEquals(TYPE_RATIONAL,
+ CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
+ assertEquals(TYPE_FLOAT,
+ CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS));
+ assertEquals(TYPE_BYTE,
+ CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
assertEquals(TYPE_INT32,
CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION));
@@ -215,35 +226,163 @@ public class CameraMetadataTest extends junit.framework.TestCase {
assertEquals(1, mMetadata.getEntryCount());
}
+ /**
+ * Format an array into a string with the {@code badIndex} highlighted with {@code **}.
+ *
+ *
Numbers are printed as hexadecimal values.
+ *
+ *
Example: {@code "[hello, **world**]"} for a {@code string[]},
+ * or a {@code "[**0xFF**, 0xFF]"} for a {@code int[]}.
+ */
+ private static String formatArray(T array, int badIndex) {
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("[");
+
+ int len = Array.getLength(array);
+ for (int i = 0; i < len; ++i) {
+
+ Object elem = Array.get(array, i);
+
+ if (i == badIndex) {
+ builder.append("**");
+ }
+
+ if (elem instanceof Number) {
+ builder.append(String.format("%x", elem));
+ } else {
+ builder.append(elem);
+ }
+
+ if (i == badIndex) {
+ builder.append("**");
+ }
+
+ if (i != len - 1) {
+ builder.append(", ");
+ }
+ }
+
+ builder.append("]");
+
+ return builder.toString();
+ }
+
private static void assertArrayEquals(T expected, T actual) {
- assertEquals(Array.getLength(expected), Array.getLength(actual));
+ if (!expected.getClass().isArray() || !actual.getClass().isArray()) {
+ throw new IllegalArgumentException("expected, actual must both be arrays");
+ }
+
+ assertEquals("Array lengths must be equal",
+ Array.getLength(expected), Array.getLength(actual));
int len = Array.getLength(expected);
for (int i = 0; i < len; ++i) {
- assertEquals(Array.get(expected, i), Array.get(actual, i));
+
+ Object expectedElement = Array.get(expected, i);
+ Object actualElement = Array.get(actual, i);
+
+ if (!expectedElement.equals(actualElement)) {
+ fail(String.format(
+ "element %d in array was not equal (expected %s, actual %s). "
+ + "Arrays were: (expected %s, actual %s).",
+ i, expectedElement, actualElement,
+ formatArray(expected, i),
+ formatArray(actual, i)));
+ }
}
}
- private void checkKeyGetAndSet(String keyStr, Class type, T value) {
- assertFalse("Use checkKeyGetAndSetArray to compare array Keys", type.isArray());
-
- Key key = new Key(keyStr, type);
+ private void checkKeyGetAndSet(String keyStr, TypeReference typeToken, T expected,
+ boolean reuse) {
+ Key key = new Key(keyStr, typeToken);
assertNull(mMetadata.get(key));
mMetadata.set(key, null);
assertNull(mMetadata.get(key));
- mMetadata.set(key, value);
+ mMetadata.set(key, expected);
T actual = mMetadata.get(key);
- assertEquals(value, actual);
+
+ if (typeToken.getRawType().isArray()) {
+ assertArrayEquals(expected, actual);
+ } else {
+ assertEquals(expected, actual);
+ }
+
+ if (reuse) {
+ // reset the key incase we want to use it again
+ mMetadata.set(key, null);
+ }
}
- private void checkKeyGetAndSetArray(String keyStr, Class type, T value) {
- assertTrue(type.isArray());
+ private void checkKeyGetAndSet(String keyStr, TypeReference typeToken, T expected) {
+ checkKeyGetAndSet(keyStr, typeToken, expected, /*reuse*/false);
+ }
- Key key = new Key(keyStr, type);
+ private void checkKeyGetAndSet(String keyStr, Class type, T expected) {
+ checkKeyGetAndSet(keyStr, TypeReference.createSpecializedTypeReference(type), expected);
+ }
+
+ /**
+ * Ensure that the data survives a marshal/unmarshal round-trip;
+ * it must also be equal to the {@code expectedNative} byte array.
+ *
+ *
As a side-effect, the metadata value corresponding to the key is now set to
+ * {@code expected}.
+ *
+ * @return key created with {@code keyName} and {@code T}
+ */
+ private Key checkKeyMarshal(String keyName, TypeReference typeReference,
+ T expected, byte[] expectedNative) {
+ Key key = new Key(keyName, typeReference);
+
+ mMetadata.set(key, null);
assertNull(mMetadata.get(key));
- mMetadata.set(key, value);
- assertArrayEquals(value, mMetadata.get(key));
+
+ // Write managed value -> make sure native bytes are what we expect
+ mMetadata.set(key, expected);
+
+ byte[] actualValues = mMetadata.readValues(key.getTag());
+ assertArrayEquals(expectedNative, actualValues);
+
+ // Write managed value -> make sure read-out managed value is what we expect
+ T actual = mMetadata.get(key);
+
+ if (typeReference.getRawType().isArray()) {
+ assertArrayEquals(expected, actual);
+ } else {
+ assertEquals(expected, actual);
+ }
+
+ // Write native bytes -> make sure read-out managed value is what we expect
+ mMetadata.writeValues(key.getTag(), expectedNative);
+ actual = mMetadata.get(key);
+
+ if (typeReference.getRawType().isArray()) {
+ assertArrayEquals(expected, actual);
+ } else {
+ assertEquals(expected, actual);
+ }
+
+ return key;
+ }
+
+ /**
+ * Ensure that the data survives a marshal/unmarshal round-trip;
+ * it must also be equal to the {@code expectedNative} byte array.
+ *
+ *
As a side-effect,
+ * the metadata value corresponding to the key is now set to {@code expected}.
+ *
+ * @return key created with {@code keyName} and {@code T}
+ */
+ private Key checkKeyMarshal(String keyName, T expected, byte[] expectedNative) {
+ @SuppressWarnings("unchecked")
+ Class expectedClass = (Class) expected.getClass();
+ return checkKeyMarshal(keyName,
+ TypeReference.createSpecializedTypeReference(expectedClass),
+ expected,
+ expectedNative);
}
@SmallTest
@@ -280,36 +419,36 @@ public class CameraMetadataTest extends junit.framework.TestCase {
@SmallTest
public void testReadWritePrimitiveArray() {
// int32 (n)
- checkKeyGetAndSetArray("android.sensor.info.sensitivityRange", int[].class,
+ checkKeyGetAndSet("android.sensor.info.sensitivityRange", int[].class,
new int[] {
0xC0FFEE, 0xDEADF00D
});
// byte (n)
- checkKeyGetAndSetArray("android.statistics.faceScores", byte[].class, new byte[] {
+ checkKeyGetAndSet("android.statistics.faceScores", byte[].class, new byte[] {
1, 2, 3, 4
});
// int64 (n)
- checkKeyGetAndSetArray("android.scaler.availableProcessedMinDurations", long[].class,
+ checkKeyGetAndSet("android.scaler.availableProcessedMinDurations", long[].class,
new long[] {
0xABCD12345678FFFFL, 0x1234ABCD5678FFFFL, 0xFFFF12345678ABCDL
});
// float (n)
- checkKeyGetAndSetArray("android.lens.info.availableApertures", float[].class,
+ checkKeyGetAndSet("android.lens.info.availableApertures", float[].class,
new float[] {
Float.MAX_VALUE, Float.MIN_NORMAL, Float.MIN_VALUE
});
// double (n) -- in particular double x 3
- checkKeyGetAndSetArray("android.jpeg.gpsCoordinates", double[].class,
+ checkKeyGetAndSet("android.jpeg.gpsCoordinates", double[].class,
new double[] {
Double.MAX_VALUE, Double.MIN_NORMAL, Double.MIN_VALUE
});
// rational (n) -- in particular rational x 9
- checkKeyGetAndSetArray("android.sensor.calibrationTransform1", Rational[].class,
+ checkKeyGetAndSet("android.sensor.calibrationTransform1", Rational[].class,
new Rational[] {
new Rational(1, 2), new Rational(3, 4), new Rational(5, 6),
new Rational(7, 8), new Rational(9, 10), new Rational(10, 11),
@@ -321,13 +460,12 @@ public class CameraMetadataTest extends junit.framework.TestCase {
*/
// bool (n) -- with TYPE_BYTE
- checkKeyGetAndSetArray("android.control.aeLock", boolean[].class, new boolean[] {
+ checkKeyGetAndSet("android.control.aeLock", boolean[].class, new boolean[] {
true, false, true
});
-
// integer (n) -- with TYPE_BYTE
- checkKeyGetAndSetArray("android.control.aeAvailableModes", int[].class, new int[] {
+ checkKeyGetAndSet("android.control.aeAvailableModes", int[].class, new int[] {
1, 2, 3, 4
});
}
@@ -345,7 +483,6 @@ public class CameraMetadataTest extends junit.framework.TestCase {
AUTO
}
- // TODO: special values for the enum.
private enum AvailableFormat {
RAW_SENSOR,
YV12,
@@ -366,7 +503,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
AeAntibandingMode.AUTO);
// byte (n)
- checkKeyGetAndSetArray("android.control.aeAvailableAntibandingModes",
+ checkKeyGetAndSet("android.control.aeAvailableAntibandingModes",
AeAntibandingMode[].class, new AeAntibandingMode[] {
AeAntibandingMode.OFF, AeAntibandingMode._50HZ, AeAntibandingMode._60HZ,
AeAntibandingMode.AUTO
@@ -376,7 +513,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
* Stranger cases that don't use byte enums
*/
// int (n)
- checkKeyGetAndSetArray("android.scaler.availableFormats", AvailableFormat[].class,
+ checkKeyGetAndSet("android.scaler.availableFormats", AvailableFormat[].class,
new AvailableFormat[] {
AvailableFormat.RAW_SENSOR,
AvailableFormat.YV12,
@@ -389,7 +526,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
@SmallTest
public void testReadWriteEnumWithCustomValues() {
- CameraMetadataNative.registerEnumValues(AeAntibandingMode.class, new int[] {
+ MarshalQueryableEnum.registerEnumValues(AeAntibandingMode.class, new int[] {
0,
10,
20,
@@ -401,15 +538,12 @@ public class CameraMetadataTest extends junit.framework.TestCase {
AeAntibandingMode.AUTO);
// byte (n)
- checkKeyGetAndSetArray("android.control.aeAvailableAntibandingModes",
+ checkKeyGetAndSet("android.control.aeAvailableAntibandingModes",
AeAntibandingMode[].class, new AeAntibandingMode[] {
AeAntibandingMode.OFF, AeAntibandingMode._50HZ, AeAntibandingMode._60HZ,
AeAntibandingMode.AUTO
});
- Key aeAntibandingModeKey =
- new Key("android.control.aeAvailableAntibandingModes",
- AeAntibandingMode[].class);
byte[] aeAntibandingModeValues = mMetadata.readValues(CameraMetadataNative
.getTag("android.control.aeAvailableAntibandingModes"));
byte[] expectedValues = new byte[] { 0, 10, 20, 30 };
@@ -420,7 +554,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
* Stranger cases that don't use byte enums
*/
// int (n)
- CameraMetadataNative.registerEnumValues(AvailableFormat.class, new int[] {
+ MarshalQueryableEnum.registerEnumValues(AvailableFormat.class, new int[] {
0x20,
0x32315659,
0x11,
@@ -429,7 +563,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
0x21,
});
- checkKeyGetAndSetArray("android.scaler.availableFormats", AvailableFormat[].class,
+ checkKeyGetAndSet("android.scaler.availableFormats", AvailableFormat[].class,
new AvailableFormat[] {
AvailableFormat.RAW_SENSOR,
AvailableFormat.YV12,
@@ -466,7 +600,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
checkKeyGetAndSet("android.jpeg.thumbnailSize", Size.class, new Size(123, 456));
// int32 x 2 x n
- checkKeyGetAndSetArray("android.scaler.availableJpegSizes", Size[].class, new Size[] {
+ checkKeyGetAndSet("android.scaler.availableJpegSizes", Size[].class, new Size[] {
new Size(123, 456),
new Size(0xDEAD, 0xF00D),
new Size(0xF00, 0xB00)
@@ -474,16 +608,217 @@ public class CameraMetadataTest extends junit.framework.TestCase {
}
@SmallTest
- public void testReadWriteRectangle() {
+ public void testReadWriteRggbChannelVector() {
// int32 x n
- checkKeyGetAndSet("android.scaler.cropRegion", Rect.class, new Rect(10, 11, 1280, 1024));
+ checkKeyMarshal("android.colorCorrection.gains",
+ new RggbChannelVector(1.0f, 2.1f, 3.2f, 4.5f),
+ toByteArray(1.0f, 2.1f, 3.2f, 4.5f));
+
+ // int32 x 2 x n [pretend; actual is not array]
+ checkKeyMarshal("android.colorCorrection.gains",
+ new RggbChannelVector[] {
+ new RggbChannelVector(1.0f, 2.0f, 3.0f, 4.0f),
+ new RggbChannelVector(9.0f, 8.0f, 7.0f, 6.0f),
+ new RggbChannelVector(1.3f, 5.5f, 2.4f, 6.7f),
+ }, toByteArray(
+ 1.0f, 2.0f, 3.0f, 4.0f,
+ 9.0f, 8.0f, 7.0f, 6.0f,
+ 1.3f, 5.5f, 2.4f, 6.7f
+ ));
+ }
+
+ @SmallTest
+ public void testReadWriteSizeF() {
+ // int32 x n
+ checkKeyMarshal("android.sensor.info.physicalSize",
+ new SizeF(123f, 456f),
+ toByteArray(123f, 456f));
// int32 x 2 x n
- checkKeyGetAndSetArray("android.statistics.faceRectangles", Rect[].class, new Rect[] {
+ checkKeyMarshal("android.sensor.info.physicalSize",
+ new SizeF[] {
+ new SizeF(123f, 456f),
+ new SizeF(1.234f, 4.567f),
+ new SizeF(999.0f, 555.0f)
+ },
+ toByteArray(
+ 123f, 456f,
+ 1.234f, 4.567f,
+ 999.0f, 555.0f)
+ );
+ }
+
+ @SmallTest
+ public void testReadWriteRectangle() {
+ // int32 x n
+ checkKeyMarshal("android.scaler.cropRegion",
+ // x1, y1, x2, y2
+ new Rect(10, 11, 1280, 1024),
+ // x, y, width, height
+ toByteArray(10, 11, 1280 - 10, 1024 - 11));
+
+ // int32 x 2 x n [actually not array, but we pretend it is]
+ checkKeyMarshal("android.scaler.cropRegion", new Rect[] {
new Rect(110, 111, 11280, 11024),
new Rect(210, 111, 21280, 21024),
new Rect(310, 111, 31280, 31024)
- });
+ }, toByteArray(
+ 110, 111, 11280 - 110, 11024 - 111,
+ 210, 111, 21280 - 210, 21024 - 111,
+ 310, 111, 31280 - 310, 31024 - 111
+ ));
+ }
+
+ @SmallTest
+ public void testReadWriteMeteringRectangle() {
+ // int32 x 5 x area_count [but we pretend it's a single element]
+ checkKeyMarshal("android.control.aeRegions",
+ new MeteringRectangle(/*x*/1, /*y*/2, /*width*/100, /*height*/200, /*weight*/5),
+ /* xmin, ymin, xmax, ymax, weight */
+ toByteArray(1, 2, 1 + 100, 2 + 200, 5));
+
+ // int32 x 5 x area_count
+ checkKeyMarshal("android.control.afRegions",
+ new MeteringRectangle[] {
+ new MeteringRectangle(/*x*/5, /*y*/6, /*width*/123, /*height*/456, /*weight*/7),
+ new MeteringRectangle(/*x*/7, /*y*/8, /*width*/456, /*height*/999, /*weight*/6),
+ new MeteringRectangle(/*x*/1, /*y*/2, /*width*/100, /*height*/200, /*weight*/5)
+ },
+ toByteArray(
+ 5, 6, 5 + 123, 6 + 456, 7,
+ 7, 8, 7 + 456, 8 + 999, 6,
+ 1, 2, 1 + 100, 2 + 200, 5
+ ));
+ }
+
+ @SmallTest
+ public void testReadWriteColorSpaceTransform() {
+ // rational x 3 x 3
+ checkKeyMarshal("android.colorCorrection.transform",
+ new ColorSpaceTransform(new Rational[] {
+ new Rational(1, 2), new Rational(3, 4), new Rational(5, 6),
+ new Rational(7, 8), new Rational(8, 9), new Rational(10, 11),
+ new Rational(1, 5), new Rational(2, 8), new Rational(3, 9),
+ }),
+ toByteArray(
+ 1, 2, 3, 4, 5, 6,
+ 7, 8, 8, 9, 10, 11,
+ 1, 5, 2, 8, 3, 9));
+ }
+
+ @SmallTest
+ public void testReadWritePoint() {
+ // int32 x 2 [actually 'x n' but pretend it's a single value for now]
+ checkKeyMarshal("android.statistics.hotPixelMap",
+ new Point(1, 2),
+ toByteArray(1, 2));
+
+ // int32 x 2 x samples
+ checkKeyMarshal("android.statistics.hotPixelMap",
+ new Point[] {
+ new Point(1, 2),
+ new Point(3, 4),
+ new Point(5, 6),
+ new Point(7, 8),
+ },
+ toByteArray(
+ 1, 2,
+ 3, 4,
+ 5, 6,
+ 7, 8)
+ );
+ }
+
+ @SmallTest
+ public void testReadWritePointF() {
+ // float x 2 [actually 'x samples' but pretend it's a single value for now]
+ checkKeyMarshal(
+ "android.sensor.profileToneCurve",
+ new PointF(1.0f, 2.0f),
+ toByteArray(1.0f, 2.0f));
+
+ // float x 2 x samples
+ checkKeyMarshal("android.sensor.profileToneCurve",
+ new PointF[] {
+ new PointF(1.0f, 2.0f),
+ new PointF(3.0f, 4.0f),
+ new PointF(5.0f, 6.0f),
+ new PointF(7.0f, 8.0f),
+ },
+ toByteArray(
+ 1.0f, 2.0f,
+ 3.0f, 4.0f,
+ 5.0f, 6.0f,
+ 7.0f, 8.0f));
+ }
+
+ @SmallTest
+ public void testReadWriteRange() {
+ // int32 x 2
+ checkKeyMarshal("android.control.aeTargetFpsRange",
+ new TypeReference>() {{ }},
+ Range.create(123, 456),
+ toByteArray(123, 456));
+
+ // int64 x 2
+ checkKeyMarshal("android.sensor.info.exposureTimeRange",
+ new TypeReference>() {{ }},
+ Range.create(123L, 456L),
+ toByteArray(123L, 456L));
+ }
+
+ @SmallTest
+ public void testReadWriteStreamConfiguration() {
+ // int32 x 4 x n
+ checkKeyMarshal("android.scaler.availableStreamConfigurations",
+ new StreamConfiguration[] {
+ new StreamConfiguration(ImageFormat.YUV_420_888, 640, 480, /*input*/false),
+ new StreamConfiguration(ImageFormat.RGB_565, 320, 240, /*input*/true),
+ },
+ toByteArray(
+ ImageFormat.YUV_420_888, 640, 480, /*input*/0,
+ ImageFormat.RGB_565, 320, 240, /*input*/1)
+ );
+ }
+
+ @SmallTest
+ public void testReadWriteStreamConfigurationDuration() {
+ // Avoid sign extending ints when converting to a long
+ final long MASK_UNSIGNED_INT = 0x00000000ffffffffL;
+
+ // int64 x 4 x n
+ checkKeyMarshal("android.scaler.availableMinFrameDurations",
+ new StreamConfigurationDuration[] {
+ new StreamConfigurationDuration(
+ ImageFormat.YUV_420_888, 640, 480, /*duration*/123L),
+ new StreamConfigurationDuration(
+ ImageFormat.RGB_565, 320, 240, /*duration*/345L),
+ },
+ toByteArray(
+ ImageFormat.YUV_420_888 & MASK_UNSIGNED_INT, 640L, 480L, /*duration*/123L,
+ ImageFormat.RGB_565 & MASK_UNSIGNED_INT, 320L, 240L, /*duration*/345L)
+ );
+ }
+
+
+ @SmallTest
+ public void testReadWriteReprocessFormatsMap() {
+
+ final int RAW_OPAQUE = 0x24;
+ final int RAW16 = ImageFormat.RAW_SENSOR;
+ final int YUV_420_888 = ImageFormat.YUV_420_888;
+ final int BLOB = 0x21;
+
+ int[] contents = new int[] {
+ RAW_OPAQUE, 3, RAW16, YUV_420_888, BLOB,
+ RAW16, 2, YUV_420_888, BLOB,
+ };
+
+ // int32 x n
+ checkKeyMarshal("android.scaler.availableInputOutputFormatsMap",
+ new ReprocessFormatsMap(contents),
+ toByteArray(contents)
+ );
}
@SmallTest
@@ -525,10 +860,6 @@ public class CameraMetadataTest extends junit.framework.TestCase {
assertArrayEquals(gpsBytes, actualBytes2);
}
- void compareGeneric(T expected, T actual) {
- assertEquals(expected, actual);
- }
-
@SmallTest
public void testReadWriteOverride() {
//
@@ -691,7 +1022,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
*/
private void validateArrayMetadataReadWriteOverride(Key key, T writeValues,
T readValues, int tag) {
- Class type = key.getType();
+ Class> type = writeValues.getClass();
if (!type.isArray()) {
throw new IllegalArgumentException("This function expects an key with array type");
} else if (type != int[].class && type != long[].class) {
@@ -737,4 +1068,20 @@ public class CameraMetadataTest extends junit.framework.TestCase {
assertNotNull(key.getName() + " result shouldn't be null", result);
assertArrayEquals(writeValues, result);
}
+
+ // TODO: move somewhere else
+ @SmallTest
+ public void testToByteArray() {
+ assertArrayEquals(new byte[] { 5, 0, 0, 0, 6, 0, 0, 0 },
+ toByteArray(5, 6));
+ assertArrayEquals(new byte[] { 5, 0, 6, 0, },
+ toByteArray((short)5, (short)6));
+ assertArrayEquals(new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
+ (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,},
+ toByteArray(~0, ~0));
+
+ assertArrayEquals(new byte[] { (byte)0xAB, (byte)0xFF, 0, 0,
+ 0x0D, (byte)0xF0, (byte)0xAD, (byte)0xDE },
+ toByteArray(0xFFAB, 0xDEADF00D));
+ }
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java
new file mode 100644
index 0000000000000..1b765eabd1c83
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java
@@ -0,0 +1,223 @@
+/*
+ * 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 com.android.mediaframeworktest.unit;
+
+import static android.hardware.camera2.utils.TypeReference.*;
+
+import android.hardware.camera2.utils.TypeReference;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.lang.reflect.Type;
+import java.util.List;
+
+public class CameraUtilsTypeReferenceTest extends junit.framework.TestCase {
+ private static final String TAG = CameraUtilsTypeReferenceTest.class.getSimpleName();
+ private static final boolean VERBOSE = false;
+
+ private class RegularClass {}
+ private class SubClass extends RegularClass {}
+
+ private class GenericClass {}
+ private class SubGenericClass extends GenericClass {}
+
+ private class SpecificClass extends GenericClass {}
+
+ private interface RegularInterface {}
+ private interface GenericInterface {}
+ private interface GenericInterface2 {}
+
+ private class ImplementsRegularInterface implements RegularInterface {}
+ private class ImplementsGenericInterface implements GenericInterface {}
+ private class Implements2GenericInterface
+ implements GenericInterface, GenericInterface2 {}
+
+ private class GenericOuterClass