diff options
| author | Igor Murashkin <iam@google.com> | 2014-04-22 15:05:50 -0700 |
|---|---|---|
| committer | Igor Murashkin <iam@google.com> | 2014-05-13 15:28:24 -0700 |
| commit | 3c40a046cf0ea7b6af01ec93e5276eccb3234bfe (patch) | |
| tree | b78afeb203d25c370995da98601eca721fbcd7e6 /core/java | |
| parent | bee74c2b479153bb6a4b9e03f068658042a5fdfc (diff) | |
| download | frameworks_base-3c40a046cf0ea7b6af01ec93e5276eccb3234bfe.zip frameworks_base-3c40a046cf0ea7b6af01ec93e5276eccb3234bfe.tar.gz frameworks_base-3c40a046cf0ea7b6af01ec93e5276eccb3234bfe.tar.bz2 | |
camera2: Update native<->managed camera metadata marshalers
* Improve existing marshalers:
- each managed/native type combination can be queried marshal support
- marshalers can recursively call other marshalers for nested types
- support marshaling/unmarshaling generic classes by using super type tokens
* Add new marshalers for:
- ColorSpaceTransform
- MeteringRectangle
- Parcelable
- Range<T>
- ReprocessFormatsMap
- RggbChannelVector
- SizeF
- StreamConfiguration
- StreamConfigurationDuration
Batteries included; so are unit tests.
Bug: 14628001
Change-Id: I38d3e646ccfb3953898cd6f750c33e4097328482
Diffstat (limited to 'core/java')
41 files changed, 3092 insertions, 691 deletions
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 6e38a22..6659278 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -17,6 +17,7 @@ package android.hardware.camera2; import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.utils.TypeReference; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -126,11 +127,13 @@ public abstract class CameraMetadata { return keyList; } + // TODO: make final or abstract public static class Key<T> { private boolean mHasTag; private int mTag; private final Class<T> mType; + private final TypeReference<T> mTypeReference; private final String mName; /** @@ -144,6 +147,22 @@ public abstract class CameraMetadata { } mName = name; mType = type; + mTypeReference = TypeReference.createSpecializedTypeReference(type); + } + + /** + * @hide + */ + @SuppressWarnings("unchecked") + public Key(String name, TypeReference<T> typeReference) { + if (name == null) { + throw new NullPointerException("Key needs a valid name"); + } else if (typeReference == null) { + throw new NullPointerException("TypeReference needs to be non-null"); + } + mName = name; + mType = (Class<T>)typeReference.getRawType(); + mTypeReference = typeReference; } public final String getName() { @@ -152,11 +171,10 @@ public abstract class CameraMetadata { @Override public final int hashCode() { - return mName.hashCode(); + return mName.hashCode() ^ mTypeReference.hashCode(); } @Override - @SuppressWarnings("unchecked") public final boolean equals(Object o) { if (this == o) { return true; @@ -166,9 +184,8 @@ public abstract class CameraMetadata { return false; } - Key lhs = (Key) o; - - return mName.equals(lhs.mName) && mType.equals(lhs.mType); + Key<?> lhs = (Key<?>)o; + return mName.equals(lhs.mName) && mTypeReference.equals(lhs.mTypeReference); } /** @@ -192,11 +209,29 @@ public abstract class CameraMetadata { } /** + * Get the raw class backing the type {@code T} for this key. + * + * <p>The distinction is only important if {@code T} is a generic, e.g. + * {@code Range<Integer>} since the nested type will be erased.</p> + * * @hide */ public final Class<T> getType() { + // TODO: remove this; other places should use #getTypeReference() instead return mType; } + + /** + * Get the type reference backing the type {@code T} for this key. + * + * <p>The distinction is only important if {@code T} is a generic, e.g. + * {@code Range<Integer>} since the nested type will be retained.</p> + * + * @hide + */ + public final TypeReference<T> getTypeReference() { + return mTypeReference; + } } /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ diff --git a/core/java/android/hardware/camera2/ColorSpaceTransform.java b/core/java/android/hardware/camera2/ColorSpaceTransform.java index 9912e4b..5e4c0a2 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 2c224f6..2b0108c 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 ff7a745..bb8e5b1 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 77b8c26..693ee2b 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 { * <p>A reduced form of a Rational is calculated by dividing both the numerator and the * denominator by their greatest common divisor.</p> * - * <pre> + * <pre>{@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 - * </pre> + * }</pre> * * @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; - - long asLong = INT_MASK & mNumerator; - asLong <<= 32; - - asLong |= (INT_MASK & mDenominator); + // Bias the hash code for the first (2^16) values for both numerator and denominator + int numeratorFlipped = mNumerator << 16 | mNumerator >>> 16; - 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 c6c59d4..894a499 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 c53dd7c..a514034 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 189ae62..6a31156 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 e24fd1b..5ddd7d6 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 ee20d68..2958ebf 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 c5e5753..9a06e97 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> T get(Key<T> 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 <T> int packSingleNative(T value, ByteBuffer buffer, Class<T> 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 <T> int packSingle(T value, ByteBuffer buffer, Class<T> 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<Enum>)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 <T extends Enum<T>> int packEnum(T value, ByteBuffer buffer, Class<T> 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 <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType, - boolean sizeOnly) { - - MetadataMarshalClass<T> 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 <T> int packArray(T value, ByteBuffer buffer, Class<T> type, int nativeType, - boolean sizeOnly) { - - int size = 0; - int arrayLength = Array.getLength(value); - - @SuppressWarnings("unchecked") - Class<Object> componentType = (Class<Object>)type.getComponentType(); - - for (int i = 0; i < arrayLength; ++i) { - size += packSingle(Array.get(value, i), buffer, componentType, nativeType, sizeOnly); - } - - return size; - } - - @SuppressWarnings("unchecked") - private static <T> T unpackSingleNative(ByteBuffer buffer, Class<T> type, int nativeType) { - - T val; - - if (nativeType == TYPE_BYTE) { - val = (T) (Byte) buffer.get(); - } else if (nativeType == TYPE_INT32) { - val = (T) (Integer) buffer.getInt(); - } else if (nativeType == TYPE_FLOAT) { - val = (T) (Float) buffer.getFloat(); - } else if (nativeType == TYPE_INT64) { - val = (T) (Long) buffer.getLong(); - } else if (nativeType == TYPE_DOUBLE) { - val = (T) (Double) buffer.getDouble(); - } else if (nativeType == TYPE_RATIONAL) { - val = (T) new Rational(buffer.getInt(), buffer.getInt()); - } else { - throw new UnsupportedOperationException("Unknown type, can't unpack a native type " - + nativeType); - } - - /** - * Rewrite types when the native type doesn't match the managed type - * - Byte -> Boolean - * - Byte -> Integer - */ - - if (nativeType == TYPE_BYTE && type == Boolean.TYPE) { - // Since a boolean can't be cast to byte, and we don't want to use getBoolean - byte asByte = (Byte) val; - boolean asBool = asByte != 0; - val = (T) (Boolean) asBool; - } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) { - byte asByte = (Byte) val; - int asInt = asByte; - val = (T) (Integer) asInt; - } else if (type != getExpectedType(nativeType)) { - throw new UnsupportedOperationException("Tried to unpack a type of " + type + - " but we expected the type to be " + getExpectedType(nativeType)); - } - - return val; - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private static <T> T unpackSingle(ByteBuffer buffer, Class<T> type, int nativeType) { - - if (type.isPrimitive() || type == Rational.class) { - return unpackSingleNative(buffer, type, nativeType); - } - - if (type.isEnum()) { - return (T) unpackEnum(buffer, (Class<Enum>)type, nativeType); - } - - if (type.isArray()) { - return unpackArray(buffer, type, nativeType); - } - - T instance = unpackClass(buffer, type, nativeType); - - return instance; - } - - private static <T extends Enum<T>> T unpackEnum(ByteBuffer buffer, Class<T> type, - int nativeType) { - int ordinal = unpackSingleNative(buffer, Integer.TYPE, nativeType); - return getEnumFromValue(type, ordinal); - } - - private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) { - - MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType); - if (marshaler == null) { - throw new IllegalArgumentException("Unknown class type: " + type); - } - - return marshaler.unmarshal(buffer, nativeType); - } - - @SuppressWarnings("unchecked") - private static <T> T unpackArray(ByteBuffer buffer, Class<T> type, int nativeType) { - - Class<?> componentType = type.getComponentType(); - Object array; - - int elementSize = getTypeSize(nativeType); - - MetadataMarshalClass<?> marshaler = getMarshaler(componentType, nativeType); - if (marshaler != null) { - elementSize = marshaler.getNativeSize(nativeType); - } - - if (elementSize != MetadataMarshalClass.NATIVE_SIZE_DYNAMIC) { - int remaining = buffer.remaining(); - int arraySize = remaining / elementSize; - - 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, type)); - } - - array = Array.newInstance(componentType, arraySize); - for (int i = 0; i < arraySize; ++i) { - Object elem = unpackSingle(buffer, componentType, nativeType); - Array.set(array, i, elem); - } - } else { - // Dynamic size, use an array list. - ArrayList<Object> arrayList = new ArrayList<Object>(); - - int primitiveSize = getTypeSize(nativeType); - while (buffer.remaining() >= primitiveSize) { - Object elem = unpackSingle(buffer, componentType, nativeType); - arrayList.add(elem); - } - - array = arrayList.toArray((T[]) Array.newInstance(componentType, 0)); - } - - if (buffer.remaining() != 0) { - Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking " - + type); - } - - return (T) array; - } - private <T> T getBase(Key<T> key) { int tag = key.getTag(); byte[] values = readValues(tag); @@ -445,10 +192,9 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { return null; } - int nativeType = getNativeType(tag); - + Marshaler<T> marshaler = getMarshalerForKey(key); ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); - return unpackSingle(buffer, key.getType(), nativeType); + return marshaler.unmarshal(buffer); } // Need overwrite some metadata that has different definitions between native @@ -632,19 +378,19 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { int tag = key.getTag(); if (value == null) { - writeValues(tag, null); + // Erase the entry + writeValues(tag, /*src*/null); return; - } - - int nativeType = getNativeType(tag); + } // else update the entry to a new value - int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true); + Marshaler<T> marshaler = getMarshalerForKey(key); + int size = marshaler.calculateMarshalSize(value); // TODO: Optimization. Cache the byte[] and reuse if the size is big enough. byte[] values = new byte[size]; ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); - packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false); + marshaler.marshal(value, buffer); writeValues(tag, values); } @@ -870,125 +616,64 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { } } - private static final HashMap<Class<? extends Enum>, int[]> sEnumValues = - new HashMap<Class<? extends Enum>, int[]>(); /** - * Register a non-sequential set of values to be used with the pack/unpack functions. - * This enables get/set to correctly marshal the enum into a value that is C-compatible. + * Get the marshaler compatible with the {@code key} and type {@code T}. * - * @param enumType The class for an enum - * @param values A list of values mapping to the ordinals of the enum - * - * @hide + * @throws UnsupportedOperationException + * if the native/managed type combination for {@code key} is not supported */ - public static <T extends Enum<T>> void registerEnumValues(Class<T> 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 <T extends Enum<T>> int getEnumValue(T enumValue) { - int[] values; - values = sEnumValues.get(enumValue.getClass()); - - int ordinal = enumValue.ordinal(); - if (values != null) { - return values[ordinal]; - } - - return ordinal; + private static <T> Marshaler<T> getMarshalerForKey(Key<T> key) { + return MarshalRegistry.getMarshaler(key.getTypeReference(), + getNativeType(key.getTag())); } - /** - * 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 extends Enum<T>> T getEnumFromValue(Class<T> 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; + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static void registerAllMarshalers() { + if (VERBOSE) { + Log.v(TAG, "Shall register metadata marshalers"); } - 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))); + MarshalQueryable[] queryList = new MarshalQueryable[] { + // marshalers for standard types + new MarshalQueryablePrimitive(), + new MarshalQueryableEnum(), + new MarshalQueryableArray(), + + // pseudo standard types, that expand/narrow the native type into a managed type + new MarshalQueryableBoolean(), + new MarshalQueryableNativeByteToInteger(), + + // marshalers for custom types + new MarshalQueryableRect(), + new MarshalQueryableSize(), + new MarshalQueryableSizeF(), + new MarshalQueryableString(), + new MarshalQueryableReprocessFormatsMap(), + new MarshalQueryableRange(), + new MarshalQueryableMeteringRectangle(), + new MarshalQueryableColorSpaceTransform(), + new MarshalQueryableStreamConfiguration(), + new MarshalQueryableStreamConfigurationDuration(), + new MarshalQueryableRggbChannelVector(), + + // generic parcelable marshaler (MUST BE LAST since it has lowest priority) + new MarshalQueryableParcelable(), + }; + + for (MarshalQueryable query : queryList) { + MarshalRegistry.registerMarshalQueryable(query); } - - return values[ordinal]; - } - - static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new - HashMap<Class<?>, MetadataMarshalClass<?>>(); - - private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) { - sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler); - } - - @SuppressWarnings("unchecked") - private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) { - MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type); - - if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) { - throw new UnsupportedOperationException("Unsupported type " + nativeType + - " to be marshalled to/from a " + type); + if (VERBOSE) { + Log.v(TAG, "Registered metadata marshalers"); } - - return marshaler; } - /** - * We use a class initializer to allow the native code to cache some field offsets - */ static { + /* + * We use a class initializer to allow the native code to cache some field offsets + */ nativeClassInit(); - - if (VERBOSE) { - Log.v(TAG, "Shall register metadata marshalers"); - } - - // load built-in marshallers - registerMarshaler(new MetadataMarshalRect()); - registerMarshaler(new MetadataMarshalSize()); - registerMarshaler(new MetadataMarshalString()); - - if (VERBOSE) { - Log.v(TAG, "Registered metadata marshalers"); - } + registerAllMarshalers(); } } diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java b/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java deleted file mode 100644 index 6d224ef..0000000 --- a/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.hardware.camera2.impl; - -import java.nio.ByteBuffer; - -public interface MetadataMarshalClass<T> { - - /** - * Marshal the specified object instance (value) into a byte buffer. - * - * @param value the value of type T that we wish to write into the byte buffer - * @param buffer the byte buffer into which the marshalled object will be written - * @param nativeType the native type, e.g. - * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}. - * Guaranteed to be one for which isNativeTypeSupported returns true. - * @param sizeOnly if this is true, don't write to the byte buffer. calculate the size only. - * @return the size that needs to be written to the byte buffer - */ - int marshal(T value, ByteBuffer buffer, int nativeType, boolean sizeOnly); - - /** - * Unmarshal a new object instance from the byte buffer. - * @param buffer the byte buffer, from which we will read the object - * @param nativeType the native type, e.g. - * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}. - * Guaranteed to be one for which isNativeTypeSupported returns true. - * @return a new instance of type T read from the byte buffer - */ - T unmarshal(ByteBuffer buffer, int nativeType); - - Class<T> getMarshalingClass(); - - /** - * Determines whether or not this marshaller supports this native type. Most marshallers - * will are likely to only support one type. - * - * @param nativeType the native type, e.g. - * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE} - * @return true if it supports, false otherwise - */ - boolean isNativeTypeSupported(int nativeType); - - public static int NATIVE_SIZE_DYNAMIC = -1; - - /** - * How many bytes T will take up if marshalled to/from nativeType - * @param nativeType the native type, e.g. - * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE} - * @return a size in bytes, or NATIVE_SIZE_DYNAMIC if the size is dynamic - */ - int getNativeSize(int nativeType); -} diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java b/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java deleted file mode 100644 index ab72c4f..0000000 --- a/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.hardware.camera2.impl; - -import android.graphics.Rect; - -import java.nio.ByteBuffer; - -public class MetadataMarshalRect implements MetadataMarshalClass<Rect> { - private static final int SIZE = 16; - - @Override - public int marshal(Rect value, ByteBuffer buffer, int nativeType, boolean sizeOnly) { - if (sizeOnly) { - return SIZE; - } - - buffer.putInt(value.left); - buffer.putInt(value.top); - buffer.putInt(value.width()); - buffer.putInt(value.height()); - - return SIZE; - } - - @Override - public Rect unmarshal(ByteBuffer buffer, int nativeType) { - - 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 Class<Rect> getMarshalingClass() { - return Rect.class; - } - - @Override - public boolean isNativeTypeSupported(int nativeType) { - return nativeType == CameraMetadataNative.TYPE_INT32; - } - - @Override - public int getNativeSize(int nativeType) { - return SIZE; - } -} diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java b/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java deleted file mode 100644 index e8143e0..0000000 --- a/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.hardware.camera2.impl; - -import android.hardware.camera2.Size; - -import java.nio.ByteBuffer; - -public class MetadataMarshalSize implements MetadataMarshalClass<Size> { - - private static final int SIZE = 8; - - @Override - public int marshal(Size value, ByteBuffer buffer, int nativeType, boolean sizeOnly) { - if (sizeOnly) { - return SIZE; - } - - buffer.putInt(value.getWidth()); - buffer.putInt(value.getHeight()); - - return SIZE; - } - - @Override - public Size unmarshal(ByteBuffer buffer, int nativeType) { - int width = buffer.getInt(); - int height = buffer.getInt(); - - return new Size(width, height); - } - - @Override - public Class<Size> getMarshalingClass() { - return Size.class; - } - - @Override - public boolean isNativeTypeSupported(int nativeType) { - return nativeType == CameraMetadataNative.TYPE_INT32; - } - - @Override - public int getNativeSize(int nativeType) { - return SIZE; - } -} diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalString.java b/core/java/android/hardware/camera2/impl/MetadataMarshalString.java deleted file mode 100644 index b61b8d3..0000000 --- a/core/java/android/hardware/camera2/impl/MetadataMarshalString.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.hardware.camera2.impl; - -import java.nio.ByteBuffer; -import java.nio.charset.Charset; - -public class MetadataMarshalString implements MetadataMarshalClass<String> { - - private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); - - @Override - public int marshal(String value, ByteBuffer buffer, int nativeType, boolean sizeOnly) { - byte[] arr = value.getBytes(UTF8_CHARSET); - - if (!sizeOnly) { - buffer.put(arr); - buffer.put((byte)0); // metadata strings are NULL-terminated - } - - return arr.length + 1; - } - - @Override - public String unmarshal(ByteBuffer buffer, int nativeType) { - - buffer.mark(); // save the current position - - boolean foundNull = false; - int stringLength = 0; - while (buffer.hasRemaining()) { - if (buffer.get() == (byte)0) { - foundNull = true; - break; - } - - stringLength++; - } - if (!foundNull) { - throw new IllegalArgumentException("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 Class<String> getMarshalingClass() { - return String.class; - } - - @Override - public boolean isNativeTypeSupported(int nativeType) { - return nativeType == CameraMetadataNative.TYPE_BYTE; - } - - @Override - public int getNativeSize(int nativeType) { - return NATIVE_SIZE_DYNAMIC; - } -} diff --git a/core/java/android/hardware/camera2/marshal/MarshalHelpers.java b/core/java/android/hardware/camera2/marshal/MarshalHelpers.java new file mode 100644 index 0000000..fd72ee2 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/MarshalHelpers.java @@ -0,0 +1,243 @@ +/* + * 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 static android.hardware.camera2.impl.CameraMetadataNative.*; +import static com.android.internal.util.Preconditions.*; + +import android.hardware.camera2.Rational; +import android.hardware.camera2.impl.CameraMetadataNative; + +/** + * Static functions in order to help implementing various marshaler functionality. + * + * <p>The intention is to statically import everything from this file into another file when + * implementing a new marshaler (or marshal queryable).</p> + * + * <p>The helpers are centered around providing primitive knowledge of the native types, + * such as the native size, the managed class wrappers, and various precondition checks.</p> + */ +public final class MarshalHelpers { + + public static final int SIZEOF_BYTE = 1; + public static final int SIZEOF_INT32 = Integer.SIZE / Byte.SIZE; + public static final int SIZEOF_INT64 = Long.SIZE / Byte.SIZE; + public static final int SIZEOF_FLOAT = Float.SIZE / Byte.SIZE; + public static final int SIZEOF_DOUBLE = Double.SIZE / Byte.SIZE; + public static final int SIZEOF_RATIONAL = SIZEOF_INT32 * 2; + + /** + * Get the size in bytes for the native camera metadata type. + * + * <p>This used to determine how many bytes it would take to encode/decode a single value + * of that {@link nativeType}.</p> + * + * @param nativeType the native type, e.g. + * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}. + * @return size in bytes >= 1 + * + * @throws UnsupportedOperationException if nativeType was not one of the built-in types + */ + public static int getPrimitiveTypeSize(int nativeType) { + switch (nativeType) { + case TYPE_BYTE: + return SIZEOF_BYTE; + case TYPE_INT32: + return SIZEOF_INT32; + case TYPE_FLOAT: + return SIZEOF_FLOAT; + case TYPE_INT64: + return SIZEOF_INT64; + case TYPE_DOUBLE: + return SIZEOF_DOUBLE; + case TYPE_RATIONAL: + return SIZEOF_RATIONAL; + } + + throw new UnsupportedOperationException("Unknown type, can't get size for " + + nativeType); + } + + + /** + * Ensure that the {@code klass} is one of the metadata-primitive classes. + * + * @param klass a non-{@code null} reference + * @return {@code klass} instance + * + * @throws UnsupportedOperationException if klass was not one of the built-in classes + * @throws NullPointerException if klass was null + * + * @see #isPrimitiveClass + */ + public static <T> Class<T> checkPrimitiveClass(Class<T> klass) { + checkNotNull(klass, "klass must not be null"); + + if (isPrimitiveClass(klass)) { + return klass; + } + + throw new UnsupportedOperationException("Unsupported class '" + klass + + "'; expected a metadata primitive class"); + } + + /** + * Checks whether or not {@code klass} is one of the metadata-primitive classes. + * + * <p>The following types (whether boxed or unboxed) are considered primitive: + * <ul> + * <li>byte + * <li>int + * <li>float + * <li>double + * <li>Rational + * </ul> + * </p> + * + * <p>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.</p> + * + * @param klass a {@link Class} instance; using {@code null} will return {@code false} + * @return {@code true} if primitive, {@code false} otherwise + */ + public static <T> boolean isPrimitiveClass(Class<T> 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. + * + * <p>Non-primitive classes are passed through as-is.</p> + * + * <p>For example, for a primitive {@code int.class => Integer.class}, + * but for a non-primitive {@code Rational.class => Rational.class}.</p> + * + * @param klass a {@code Class} reference + * + * @return wrapped class object, or same class object if non-primitive + */ + @SuppressWarnings("unchecked") + public static <T> Class<T> wrapClassIfPrimitive(Class<T> klass) { + if (klass == byte.class) { + return (Class<T>)Byte.class; + } else if (klass == int.class) { + return (Class<T>)Integer.class; + } else if (klass == float.class) { + return (Class<T>)Float.class; + } else if (klass == long.class) { + return (Class<T>)Long.class; + } else if (klass == double.class) { + return (Class<T>)Double.class; + } + + return klass; + } + + /** + * Return a human-readable representation of the {@code nativeType}, e.g. "TYPE_INT32" + * + * <p>Out-of-range values return a string with "UNKNOWN" as the prefix.</p> + * + * @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 0000000..35fed1f --- /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. + * + * <p>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. + * </p> + * + * <p>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}.</p> + */ +public interface MarshalQueryable<T> { + /** + * Create a marshaler between the selected managed and native type. + * + * <p>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.</p> + * + * @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<T> createMarshaler( + TypeReference<T> 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. + * + * <p>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.</p> + * + * <p>Most marshalers are likely to only support one type map.</p> + */ + public boolean isTypeMappingSupported(TypeReference<T> 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 0000000..92d9057 --- /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.</p> + */ +public class MarshalRegistry { + + /** + * Register a marshal queryable for the managed type {@code T}. + * + * <p>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}.</p> + * + * @param queryable a non-{@code null} marshal queryable that supports marshaling {@code T} + */ + public static <T> void registerMarshalQueryable(MarshalQueryable<T> queryable) { + sRegisteredMarshalQueryables.add(queryable); + } + + /** + * Lookup a marshaler between {@code T} and {@code nativeType}. + * + * <p>Marshalers are looked up in the order they were registered; earlier registered + * marshal queriers get priority.</p> + * + * @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 <T> Marshaler<T> getMarshaler(TypeReference<T> 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<T> marshalToken = new MarshalToken<T>(typeToken, nativeType); + + /* + * Marshalers are instantiated lazily once they are looked up; successive lookups + * will not instantiate new marshalers. + */ + Marshaler<T> marshaler = + (Marshaler<T>) 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<T> castedPotential = + (MarshalQueryable<T>)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<T> { + public MarshalToken(TypeReference<T> typeReference, int nativeType) { + this.typeReference = typeReference; + this.nativeType = nativeType; + } + + final TypeReference<T> 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<MarshalQueryable<?>> sRegisteredMarshalQueryables = + new ArrayList<MarshalQueryable<?>>(); + private static HashMap<MarshalToken<?>, Marshaler<?>> sMarshalerMap = + new HashMap<MarshalToken<?>, 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 0000000..eb0ad15 --- /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. + * + * <p>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}.</p> + * + * @param <T> the compile-time managed type + */ +public abstract class Marshaler<T> { + + protected final TypeReference<T> mTypeReference; + protected final int mNativeType; + + /** + * Instantiate a marshaler between a single managed/native type combination. + * + * <p>This particular managed/native type combination must be supported by + * {@link #isTypeMappingSupported}.</p> + * + * @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<T> query, TypeReference<T> 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. + * + * <p>Upon completion, the {@link ByteBuffer#position()} will have advanced by + * the {@link #calculateMarshalSize marshal size} of {@code value}.</p> + * + * @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}. + * + * <p>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()}.</p> + * + * <p>Overriding this method is a must when the size is {@link NATIVE_SIZE_DYNAMIC dynamic}.</p> + * + * @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. + * + * <p>Upon completion, the {@link ByteBuffer#position()} will have advanced by + * the {@link #calculateMarshalSize marshal size} of the returned {@code T} instance.</p> + * + * @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. + * + * <p>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.</p> + */ + 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}. + * + * <p>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.</p> + * + * <p>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[]}.</p> + * + * @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<T> 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 0000000..22b87ef --- /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}. + * + * <p>To marshal any {@code T} to/from a native type, the marshaler for T to/from that native type + * also has to exist.</p> + * + * <p>{@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).</p> + + * @param <T> the type of the array (e.g. T = int[], or T = Rational[]) + */ +public class MarshalQueryableArray<T> implements MarshalQueryable<T> { + + private static final String TAG = MarshalQueryableArray.class.getSimpleName(); + private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); + + private class MarshalerArray extends Marshaler<T> { + private final Class<T> mClass; + private final Marshaler<?> mComponentMarshaler; + private final Class<?> mComponentClass; + + @SuppressWarnings("unchecked") + protected MarshalerArray(TypeReference<T> typeReference, int nativeType) { + super(MarshalQueryableArray.this, typeReference, nativeType); + + mClass = (Class<T>)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<Object> arrayList = new ArrayList<Object>(); + + // 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 <TElem> void marshalArrayElement(Marshaler<TElem> 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 <TElem> int calculateElementMarshalSize(Marshaler<TElem> marshaler, + Object array, int index) { + Object elem = Array.get(array, index); + + return marshaler.calculateMarshalSize((TElem) elem); + } + } + + @Override + public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) { + return new MarshalerArray(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) { + // support both ConcreteType[] and GenericType<ConcreteType>[] + 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 0000000..4aa4b4a --- /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<Boolean> { + + private class MarshalerBoolean extends Marshaler<Boolean> { + protected MarshalerBoolean(TypeReference<Boolean> 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<Boolean> createMarshaler(TypeReference<Boolean> managedType, + int nativeType) { + return new MarshalerBoolean(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<Boolean> 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 0000000..d3796db --- /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<ColorSpaceTransform> { + + 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<ColorSpaceTransform> { + protected MarshalerColorSpaceTransform(TypeReference<ColorSpaceTransform> 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<ColorSpaceTransform> createMarshaler( + TypeReference<ColorSpaceTransform> managedType, int nativeType) { + return new MarshalerColorSpaceTransform(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported( + TypeReference<ColorSpaceTransform> 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 0000000..fa53db2 --- /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}. + * + * <p>Default values of the enum are mapped to its ordinal; this can be overridden + * by providing a manual value with {@link #registerEnumValues}.</p> + + * @param <T> the type of {@code Enum} + */ +public class MarshalQueryableEnum<T extends Enum<T>> implements MarshalQueryable<T> { + + 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<T> { + + private final Class<T> mClass; + + @SuppressWarnings("unchecked") + protected MarshalerEnum(TypeReference<T> typeReference, int nativeType) { + super(MarshalQueryableEnum.this, typeReference, nativeType); + + mClass = (Class<T>)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<T> createMarshaler(TypeReference<T> managedType, int nativeType) { + return new MarshalerEnum(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<T> 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<Class<? extends Enum>, int[]> sEnumValues = + new HashMap<Class<? extends Enum>, int[]>(); + + /** + * Register a non-sequential set of values to be used with the marshal/unmarshal functions. + * + * <p>This enables get/set to correctly marshal the enum into a value that is C-compatible.</p> + * + * @param enumType The class for an enum + * @param values A list of values mapping to the ordinals of the enum + */ + public static <T extends Enum<T>> void registerEnumValues(Class<T> 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. + * + * <p>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.</p> + * + * @param enumValue Enum instance + * @return Int guaranteed to be ABI-compatible with the C enum equivalent + */ + private static <T extends Enum<T>> 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 extends Enum<T>> T getEnumFromValue(Class<T> 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 0000000..c8b9bd8 --- /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<MeteringRectangle> { + private static final int SIZE = SIZEOF_INT32 * 5; + + /** (xmin, ymin, xmax, ymax, weight) */ + private class MarshalerMeteringRectangle extends Marshaler<MeteringRectangle> { + protected MarshalerMeteringRectangle(TypeReference<MeteringRectangle> 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<MeteringRectangle> createMarshaler( + TypeReference<MeteringRectangle> managedType, int nativeType) { + return new MarshalerMeteringRectangle(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported( + TypeReference<MeteringRectangle> 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 0000000..3b89c82 --- /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<Integer> { + + private static final int UINT8_MASK = (1 << Byte.SIZE) - 1; + + private class MarshalerNativeByteToInteger extends Marshaler<Integer> { + protected MarshalerNativeByteToInteger(TypeReference<Integer> 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<Integer> createMarshaler(TypeReference<Integer> managedType, + int nativeType) { + return new MarshalerNativeByteToInteger(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<Integer> 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 0000000..1fd6a1d --- /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 + * + * <p>Use with extreme caution! File descriptors and binders will not be marshaled across.</p> + */ +public class MarshalQueryableParcelable<T extends Parcelable> + implements MarshalQueryable<T> { + + 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<T> { + + private final Class<T> mClass; + private final Parcelable.Creator<T> mCreator; + + @SuppressWarnings("unchecked") + protected MarshalerParcelable(TypeReference<T> typeReference, + int nativeType) { + super(MarshalQueryableParcelable.this, typeReference, nativeType); + + mClass = (Class<T>)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<T>)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<T> createMarshaler(TypeReference<T> managedType, int nativeType) { + return new MarshalerParcelable(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<T> 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 0000000..708da70 --- /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}. + * + * <p>The following list of type marshaling is supported: + * <ul> + * <li>byte <-> TYPE_BYTE + * <li>int <-> TYPE_INT32 + * <li>long <-> TYPE_INT64 + * <li>float <-> TYPE_FLOAT + * <li>double <-> TYPE_DOUBLE + * <li>Rational <-> TYPE_RATIONAL + * </ul> + * </p> + * + * <p>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}).</p> + * + * <p>Each managed type <!--(other than boolean)--> 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.</p> + */ +public final class MarshalQueryablePrimitive<T> implements MarshalQueryable<T> { + + private class MarshalerPrimitive extends Marshaler<T> { + /** Always the wrapped class variant of the primitive class for {@code T} */ + private final Class<T> mClass; + + @SuppressWarnings("unchecked") + protected MarshalerPrimitive(TypeReference<T> typeReference, int nativeType) { + super(MarshalQueryablePrimitive.this, typeReference, nativeType); + + // Turn primitives into wrappers, otherwise int.class.cast(Integer) will fail + mClass = wrapClassIfPrimitive((Class<T>)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<T> createMarshaler(TypeReference<T> managedType, int nativeType) { + return new MarshalerPrimitive(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<T> 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 0000000..8512804 --- /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<T extends Comparable<? super T>> + implements MarshalQueryable<Range<T>> { + private static final int RANGE_COUNT = 2; + + private class MarshalerRange extends Marshaler<Range<T>> { + private final Class<? super Range<T>> mClass; + private final Constructor<Range<T>> mConstructor; + /** Marshal the {@code T} inside of {@code Range<T>} */ + private final Marshaler<T> mNestedTypeMarshaler; + + @SuppressWarnings("unchecked") + protected MarshalerRange(TypeReference<Range<T>> typeReference, + int nativeType) { + super(MarshalQueryableRange.this, typeReference, nativeType); + + mClass = typeReference.getRawType(); + + /* + * Lookup the actual type argument, e.g. Range<Integer> --> 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<T>)MarshalRegistry.getMarshaler( + actualTypeArgToken, mNativeType); + try { + mConstructor = (Constructor<Range<T>>)mClass.getConstructor( + Comparable.class, Comparable.class); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + + @Override + public void marshal(Range<T> value, ByteBuffer buffer) { + mNestedTypeMarshaler.marshal(value.getLower(), buffer); + mNestedTypeMarshaler.marshal(value.getUpper(), buffer); + } + + @Override + public Range<T> 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<T> 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<Range<T>> createMarshaler(TypeReference<Range<T>> managedType, + int nativeType) { + return new MarshalerRange(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<Range<T>> 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 0000000..de20a1f --- /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<Rect> { + private static final int SIZE = SIZEOF_INT32 * 4; + + private class MarshalerRect extends Marshaler<Rect> { + protected MarshalerRect(TypeReference<Rect> 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<Rect> createMarshaler(TypeReference<Rect> managedType, int nativeType) { + return new MarshalerRect(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<Rect> 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 0000000..3025cb4 --- /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<ReprocessFormatsMap> { + + private class MarshalerReprocessFormatsMap extends Marshaler<ReprocessFormatsMap> { + protected MarshalerReprocessFormatsMap( + TypeReference<ReprocessFormatsMap> 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<ReprocessFormatsMap> createMarshaler( + TypeReference<ReprocessFormatsMap> managedType, int nativeType) { + return new MarshalerReprocessFormatsMap(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<ReprocessFormatsMap> 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 0000000..93c0e92 --- /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<RggbChannelVector> { + private static final int SIZE = SIZEOF_FLOAT * RggbChannelVector.COUNT; + + private class MarshalerRggbChannelVector extends Marshaler<RggbChannelVector> { + protected MarshalerRggbChannelVector(TypeReference<RggbChannelVector> 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<RggbChannelVector> createMarshaler( + TypeReference<RggbChannelVector> managedType, int nativeType) { + return new MarshalerRggbChannelVector(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported( + TypeReference<RggbChannelVector> 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 0000000..6a73bee --- /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<Size> { + private static final int SIZE = SIZEOF_INT32 * 2; + + private class MarshalerSize extends Marshaler<Size> { + protected MarshalerSize(TypeReference<Size> 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<Size> createMarshaler(TypeReference<Size> managedType, int nativeType) { + return new MarshalerSize(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<Size> 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 0000000..b60a46d --- /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<SizeF> { + + private static final int SIZE = SIZEOF_FLOAT * 2; + + private class MarshalerSizeF extends Marshaler<SizeF> { + + protected MarshalerSizeF(TypeReference<SizeF> 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<SizeF> createMarshaler( + TypeReference<SizeF> managedType, int nativeType) { + return new MarshalerSizeF(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<SizeF> 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 0000000..6a4e821 --- /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} + * + * <p>Data is stored as {@code (format, width, height, input?)} tuples (int32).</p> + */ +public class MarshalQueryableStreamConfiguration + implements MarshalQueryable<StreamConfiguration> { + private static final int SIZE = SIZEOF_INT32 * 4; + + private class MarshalerStreamConfiguration extends Marshaler<StreamConfiguration> { + protected MarshalerStreamConfiguration(TypeReference<StreamConfiguration> 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<StreamConfiguration> createMarshaler( + TypeReference<StreamConfiguration> managedType, int nativeType) { + return new MarshalerStreamConfiguration(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<StreamConfiguration> 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 0000000..c3d564e --- /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. + * + * <p> + * Data is stored as {@code (format, width, height, durationNs)} tuples (int64). + * </p> + */ +public class MarshalQueryableStreamConfigurationDuration + implements MarshalQueryable<StreamConfigurationDuration> { + + 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<StreamConfigurationDuration> { + + protected MarshalerStreamConfigurationDuration( + TypeReference<StreamConfigurationDuration> 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<StreamConfigurationDuration> createMarshaler( + TypeReference<StreamConfigurationDuration> managedType, int nativeType) { + return new MarshalerStreamConfigurationDuration(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<StreamConfigurationDuration> 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 0000000..bf518bb --- /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<String> { + + 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<String> { + + protected MarshalerString(TypeReference<String> 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<String> createMarshaler( + TypeReference<String> managedType, int nativeType) { + return new MarshalerString(managedType, nativeType); + } + + @Override + public boolean isTypeMappingSupported(TypeReference<String> 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 0000000..783d0a1 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/impl/package.html @@ -0,0 +1,3 @@ +<body> +{@hide} +</body> 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 0000000..783d0a1 --- /dev/null +++ b/core/java/android/hardware/camera2/marshal/package.html @@ -0,0 +1,3 @@ +<body> +{@hide} +</body> diff --git a/core/java/android/hardware/camera2/impl/HashCodeHelpers.java b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java index 2d63827..b980549 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 0000000..d0c919c --- /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. + * + * <p>Usage example: <pre>{@code + * // using anonymous classes (preferred) + * TypeReference<Integer> intToken = new TypeReference<Integer>() {{ }}; + * + * // using named classes + * class IntTypeReference extends TypeReference<Integer> {...} + * TypeReference<Integer> intToken = new IntTypeReference(); + * }</p></pre> + * + * <p>Unlike the reference implementation, this bans nested TypeVariables; that is all + * dynamic types must equal to the static types.</p> + * + * <p>See <a href="http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html"> + * http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html</a> + * for more details.</p> + */ +public abstract class TypeReference<T> { + 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<T> + mType = thisType.getActualTypeArguments()[0]; + + /* + * Prohibit type references with type variables such as + * + * class GenericListToken<T> extends TypeReference<List<T>> + * + * 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<T> extends TypeReference<T> { + public SpecializedTypeReference(Class<T> 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. + * + * <p>As with a regular type reference, the {@code klass} must not contain + * any type variables.</p> + * + * @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 <T> TypeReference<T> createSpecializedTypeReference(Class<T> klass) { + return new SpecializedTypeReference<T>(klass); + } + + /** + * Create a specialized type reference from a dynamic {@link Type} instance, + * bypassing the standard compile-time checks. + * + * <p>As with a regular type reference, the {@code type} must not contain + * any type variables.</p> + * + * @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. + * + * <p><ul> + * <li>If T is a Class itself, T itself is returned. + * <li>If T is a ParameterizedType, the raw type of the parameterized type is returned. + * <li>If T is a GenericArrayType, the returned type is the corresponding array class. + * For example: {@code List<Integer>[]} => {@code List[]}. + * <li>If T is a type variable or a wildcard type, the raw type of the first upper bound is + * returned. For example: {@code <X extends Foo>} => {@code Foo}. + * </ul> + * + * @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. + * + * <p>A TypeReference is only equal to another TypeReference if their captured type {@code T} + * is also equal.</p> + */ + @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. + * + * <p>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<T>}).</p> + * + * <p>See <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.4"> + * http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.4</a> + * for a more formal definition of a type variable</p>. + * + * @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<T> - has a type variable + * + * This also covers the case of class Foo<T> 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<T> { + * 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<T1, T2, T3, ... Tn>" in the scope of a + * + * // no type variables here, T1-Tn are known at this definition + * class X extends Foo<T1, T2, T3, ... Tn> + * + * // T1 is a type variable, T2-Tn are known at this definition + * class X<T1> extends Foo<T1, T2, T3, ... Tn> + */ + 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<T1, T2, T3, ... Tn>" + 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. + * + * <p>Empty and null arrays trivially have no type variables.</p> + * + * @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 9a4bd4b..d7e8cf0 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. |
