diff options
20 files changed, 1005 insertions, 898 deletions
diff --git a/api/current.txt b/api/current.txt index 85fe16c..d69170f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10804,7 +10804,7 @@ package android.hardware.camera2 { method public abstract void captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract void close() throws java.lang.Exception; method public abstract void configureOutputs(java.util.List<android.view.Surface>) throws android.hardware.camera2.CameraAccessException; - method public abstract android.hardware.camera2.CaptureRequest createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException; + method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException; method public abstract void flush() throws android.hardware.camera2.CameraAccessException; method public abstract java.lang.String getId(); method public abstract android.hardware.camera2.CameraProperties getProperties() throws android.hardware.camera2.CameraAccessException; @@ -10850,14 +10850,8 @@ package android.hardware.camera2 { method public void onCameraUnavailable(java.lang.String); } - public class CameraMetadata implements java.lang.AutoCloseable android.os.Parcelable { - ctor public CameraMetadata(); - method public void close() throws java.lang.Exception; - method public int describeContents(); - method public T get(android.hardware.camera2.CameraMetadata.Key<T>); - method public void readFromParcel(android.os.Parcel); - method public void set(android.hardware.camera2.CameraMetadata.Key<T>, T); - method public void writeToParcel(android.os.Parcel, int); + public abstract class CameraMetadata { + method public abstract T get(android.hardware.camera2.CameraMetadata.Key<T>); field public static final int COLOR_CORRECTION_MODE_FAST = 1; // 0x1 field public static final int COLOR_CORRECTION_MODE_HIGH_QUALITY = 2; // 0x2 field public static final int COLOR_CORRECTION_MODE_TRANSFORM_MATRIX = 0; // 0x0 @@ -10941,7 +10935,6 @@ package android.hardware.camera2 { field public static final int CONTROL_SCENE_MODE_SUNSET = 10; // 0xa field public static final int CONTROL_SCENE_MODE_THEATRE = 7; // 0x7 field public static final int CONTROL_SCENE_MODE_UNSUPPORTED = 0; // 0x0 - field public static final android.os.Parcelable.Creator CREATOR; field public static final int EDGE_MODE_FAST = 1; // 0x1 field public static final int EDGE_MODE_HIGH_QUALITY = 2; // 0x2 field public static final int EDGE_MODE_OFF = 0; // 0x0 @@ -10974,14 +10967,13 @@ package android.hardware.camera2 { } public static class CameraMetadata.Key { - ctor public CameraMetadata.Key(java.lang.String, java.lang.Class<T>); method public final boolean equals(java.lang.Object); method public final java.lang.String getName(); method public final int hashCode(); } public final class CameraProperties extends android.hardware.camera2.CameraMetadata { - ctor public CameraProperties(); + method public T get(android.hardware.camera2.CameraMetadata.Key<T>); field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_ANTIBANDING_MODES; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_COMPENSATION_RANGE; @@ -11024,10 +11016,10 @@ package android.hardware.camera2 { } public final class CaptureRequest extends android.hardware.camera2.CameraMetadata implements android.os.Parcelable { - method public void addTarget(android.view.Surface); + method public int describeContents(); + method public T get(android.hardware.camera2.CameraMetadata.Key<T>); method public java.lang.Object getTag(); - method public void removeTarget(android.view.Surface); - method public void setTag(java.lang.Object); + method public void writeToParcel(android.os.Parcel, int); field public static final android.hardware.camera2.CameraMetadata.Key BLACK_LEVEL_LOCK; field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_GAINS; field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_MODE; @@ -11077,7 +11069,17 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MODE; } + public static final class CaptureRequest.Builder { + method public void addTarget(android.view.Surface); + method public android.hardware.camera2.CaptureRequest build(); + method public T get(android.hardware.camera2.CameraMetadata.Key<T>); + method public void removeTarget(android.view.Surface); + method public void set(android.hardware.camera2.CameraMetadata.Key<T>, T); + method public void setTag(java.lang.Object); + } + public final class CaptureResult extends android.hardware.camera2.CameraMetadata { + method public T get(android.hardware.camera2.CameraMetadata.Key<T>); field public static final android.hardware.camera2.CameraMetadata.Key BLACK_LEVEL_LOCK; field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_GAINS; field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_TRANSFORM; diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 33da80f..75c0f7d 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -230,15 +230,17 @@ public interface CameraDevice extends AutoCloseable { public void configureOutputs(List<Surface> outputs) throws CameraAccessException; /** - * <p>Create a {@link CaptureRequest} initialized with template for a target - * use case. The settings are chosen to be the best options for the specific - * camera device, so it is not recommended to reuse the same request for a - * different camera device; create a request for that device and override - * the settings as desired, instead.</p> + * <p>Create a {@link CaptureRequest.Builder} for new capture requests, + * initialized with template for a target use case. The settings are chosen + * to be the best options for the specific camera device, so it is not + * recommended to reuse the same request for a different camera device; + * create a builder specific for that device and template and override the + * settings as desired, instead.</p> * * @param templateType An enumeration selecting the use case for this * request; one of the CameraDevice.TEMPLATE_ values. - * @return a filled-in CaptureRequest, except for output streams + * @return a builder for a capture request, initialized with default + * settings for that template, and no output streams * * @throws IllegalArgumentException if the templateType is not in the list * of supported templates. @@ -252,7 +254,7 @@ public interface CameraDevice extends AutoCloseable { * @see #TEMPLATE_VIDEO_SNAPSHOT * @see #TEMPLATE_MANUAL */ - public CaptureRequest createCaptureRequest(int templateType) + public CaptureRequest.Builder createCaptureRequest(int templateType) throws CameraAccessException; /** @@ -262,10 +264,10 @@ public interface CameraDevice extends AutoCloseable { * including sensor, lens, flash, and post-processing settings.</p> * * <p>Each request will produce one {@link CaptureResult} and produce new - * frames for one or more target Surfaces, set with CaptureRequests's {@link - * CaptureRequest#addTarget}. The target surfaces must be configured as - * active outputs with {@link #configureOutputs} before calling this - * method.</p> + * frames for one or more target Surfaces, set with the CaptureRequest + * builder's {@link CaptureRequest.Builder#addTarget} method. The target + * surfaces must be configured as active outputs with + * {@link #configureOutputs} before calling this method.</p> * * <p>Multiple requests can be in progress at once. They are processed in * first-in, first-out order, with minimal delays between each @@ -303,10 +305,11 @@ public interface CameraDevice extends AutoCloseable { * calls. * * <p>The requests will be captured in order, each capture producing one - * {@link CaptureResult} and image buffers for one or more target {@link - * android.view.Surface surfaces}. The target surfaces for each request (set - * with {@link CaptureRequest#addTarget}) must be configured as active - * outputs with {@link #configureOutputs} before calling this method.</p> + * {@link CaptureResult} and image buffers for one or more target + * {@link android.view.Surface surfaces}. The target surfaces for each + * request (set with {@link CaptureRequest.Builder#addTarget}) must be + * configured as active outputs with {@link #configureOutputs} before + * calling this method.</p> * * <p>The main difference between this method and simply calling * {@link #capture} repeatedly is that this method guarantees that no diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index d294014..4ad9259 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -20,6 +20,7 @@ import android.content.Context; import android.hardware.ICameraService; import android.hardware.ICameraServiceListener; import android.hardware.IProCameraUser; +import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.utils.CameraBinderDecorator; import android.hardware.camera2.utils.CameraRuntimeException; import android.hardware.camera2.utils.BinderHolder; @@ -178,7 +179,7 @@ public final class CameraManager { // TODO: implement and call a service function to get the capabilities on C++ side // TODO: get properties from service - return new CameraProperties(); + return new CameraProperties(new CameraMetadataNative()); } /** diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 70d777f..18fffc0 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -16,20 +16,7 @@ package android.hardware.camera2; -import android.hardware.camera2.impl.MetadataMarshalClass; -import android.hardware.camera2.impl.MetadataMarshalRect; -import android.hardware.camera2.impl.MetadataMarshalSize; -import android.hardware.camera2.impl.MetadataMarshalString; -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; -import java.util.Map; +import android.hardware.camera2.impl.CameraMetadataNative; /** * The base class for camera controls and information. @@ -42,62 +29,12 @@ import java.util.Map; * @see CameraManager * @see CameraProperties **/ -public class CameraMetadata implements Parcelable, AutoCloseable { - - public CameraMetadata() { - mMetadataMap = new HashMap<Key<?>, Object>(); - - mMetadataPtr = nativeAllocate(); - if (mMetadataPtr == 0) { - throw new OutOfMemoryError("Failed to allocate native CameraMetadata"); - } - } - - public static final Parcelable.Creator<CameraMetadata> CREATOR = - new Parcelable.Creator<CameraMetadata>() { - @Override - public CameraMetadata createFromParcel(Parcel in) { - CameraMetadata metadata = new CameraMetadata(); - metadata.readFromParcel(in); - return metadata; - } - - @Override - public CameraMetadata[] newArray(int size) { - return new CameraMetadata[size]; - } - }; - - private static final String TAG = "CameraMetadataJV"; +public abstract class CameraMetadata { /** - * Set a camera metadata field to a value. The field definitions can be - * found in {@link CameraProperties}, {@link CaptureResult}, and - * {@link CaptureRequest}. - * - * @param key The metadata field to write. - * @param value The value to set the field to, which must be of a matching - * type to the key. + * @hide */ - public <T> void set(Key<T> key, T value) { - int tag = key.getTag(); - - if (value == null) { - writeValues(tag, null); - return; - } - - int nativeType = getNativeType(tag); - - int size = packSingle(value, null, key.mType, nativeType, /* sizeOnly */true); - - // 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.mType, nativeType, /*sizeOnly*/false); - - writeValues(tag, values); + protected CameraMetadata() { } /** @@ -110,342 +47,16 @@ public class CameraMetadata implements Parcelable, AutoCloseable { * @param key The metadata field to read. * @return The value of that key, or {@code null} if the field is not set. */ - @SuppressWarnings("unchecked") - public <T> T get(Key<T> key) { - int tag = key.getTag(); - byte[] values = readValues(tag); - if (values == null) { - return null; - } - - int nativeType = getNativeType(tag); - - ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); - return unpackSingle(buffer, key.mType, nativeType); - } - - // Keep up-to-date with camera_metadata.h - /** - * @hide - */ - public static final int TYPE_BYTE = 0; - /** - * @hide - */ - public static final int TYPE_INT32 = 1; - /** - * @hide - */ - public static final int TYPE_FLOAT = 2; - /** - * @hide - */ - public static final int TYPE_INT64 = 3; - /** - * @hide - */ - public static final int TYPE_DOUBLE = 4; - /** - * @hide - */ - public static final int TYPE_RATIONAL = 5; - /** - * @hide - */ - public static final int NUM_TYPES = 6; - - 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; - - 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; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - nativeWriteToParcel(dest); - } - - /** - * Expand this object from a Parcel. - * @param in The Parcel from which the object should be read - */ - public void readFromParcel(Parcel in) { - nativeReadFromParcel(in); - } + public abstract <T> T get(Key<T> key); public static class Key<T> { private boolean mHasTag; private int mTag; private final Class<T> mType; + private final String mName; - /* + /** * @hide */ public Key(String name, Class<T> type) { @@ -483,8 +94,6 @@ public class CameraMetadata implements Parcelable, AutoCloseable { return mName.equals(lhs.mName); } - private final String mName; - /** * <p> * Get the tag corresponding to this key. This enables insertion into the @@ -499,265 +108,18 @@ public class CameraMetadata implements Parcelable, AutoCloseable { */ public final int getTag() { if (!mHasTag) { - mTag = CameraMetadata.getTag(mName); + mTag = CameraMetadataNative.getTag(mName); mHasTag = true; } return mTag; } - } - - private final Map<Key<?>, Object> mMetadataMap; - - private long mMetadataPtr; // native CameraMetadata* - - private native long nativeAllocate(); - private native synchronized void nativeWriteToParcel(Parcel dest); - private native synchronized void nativeReadFromParcel(Parcel source); - private native synchronized void nativeSwap(CameraMetadata other) throws NullPointerException; - private native synchronized void nativeClose(); - private native synchronized boolean nativeIsEmpty(); - private native synchronized int nativeGetEntryCount(); - - private native synchronized byte[] nativeReadValues(int tag); - private native synchronized void nativeWriteValues(int tag, byte[] src); - - private static native int nativeGetTagFromKey(String keyName) - throws IllegalArgumentException; - private static native int nativeGetTypeFromTag(int tag) - throws IllegalArgumentException; - private static native void nativeClassInit(); - - /** - * <p>Perform a 0-copy swap of the internal metadata with another object.</p> - * - * <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p> - * - * @param other Metadata to swap with - * @throws NullPointerException if other was null - * @hide - */ - public void swap(CameraMetadata other) { - nativeSwap(other); - } - - /** - * @hide - */ - public int getEntryCount() { - return nativeGetEntryCount(); - } - - /** - * Does this metadata contain at least 1 entry? - * - * @hide - */ - public boolean isEmpty() { - return nativeIsEmpty(); - } - - /** - * <p>Closes this object, and releases all native resources associated with it.</p> - * - * <p>Calling any other public method after this will result in an IllegalStateException - * being thrown.</p> - */ - @Override - public void close() throws Exception { - // this sets mMetadataPtr to 0 - nativeClose(); - mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final - } - - /** - * Whether or not {@link #close} has already been called (at least once) on this object. - * @hide - */ - public boolean isClosed() { - synchronized (this) { - return mMetadataPtr == 0; - } - } - - /** - * Convert a key string into the equivalent native tag. - * - * @throws IllegalArgumentException if the key was not recognized - * @throws NullPointerException if the key was null - * - * @hide - */ - public static int getTag(String key) { - return nativeGetTagFromKey(key); - } - - /** - * Get the underlying native type for a tag. - * - * @param tag An integer tag, see e.g. {@link #getTag} - * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE} - * - * @hide - */ - public static int getNativeType(int tag) { - return nativeGetTypeFromTag(tag); - } - - /** - * <p>Updates the existing entry for tag with the new bytes pointed by src, erasing - * the entry if src was null.</p> - * - * <p>An empty array can be passed in to update the entry to 0 elements.</p> - * - * @param tag An integer tag, see e.g. {@link #getTag} - * @param src An array of bytes, or null to erase the entry - * - * @hide - */ - public void writeValues(int tag, byte[] src) { - nativeWriteValues(tag, src); - } - - /** - * <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize - * the data properly.</p> - * - * <p>An empty array can be returned to denote an existing entry with 0 elements.</p> - * - * @param tag An integer tag, see e.g. {@link #getTag} - * - * @return {@code null} if there were 0 entries for this tag, a byte[] otherwise. - * @hide - */ - public byte[] readValues(int tag) { - // TODO: Optimization. Native code returns a ByteBuffer instead. - return nativeReadValues(tag); - } - - @Override - protected void finalize() throws Throwable { - try { - close(); - } finally { - super.finalize(); - } - } - - 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. - * - * @param enumType The class for an enum - * @param values A list of values mapping to the ordinals of the enum - * - * @hide - */ - 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); - } - - 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; - } - - /** - * 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]; - } - 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); + /** + * @hide + */ + public final Class<T> getType() { + return mType; } - - return marshaler; - } - - /** - * We use a class initializer to allow the native code to cache some field offsets - */ - static { - System.loadLibrary("media_jni"); - nativeClassInit(); - - Log.v(TAG, "Shall register metadata marshalers"); - - // load built-in marshallers - registerMarshaler(new MetadataMarshalRect()); - registerMarshaler(new MetadataMarshalSize()); - registerMarshaler(new MetadataMarshalString()); - - Log.v(TAG, "Registered metadata marshalers"); } /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ diff --git a/core/java/android/hardware/camera2/CameraProperties.java b/core/java/android/hardware/camera2/CameraProperties.java index ebbdd88..45c009f 100644 --- a/core/java/android/hardware/camera2/CameraProperties.java +++ b/core/java/android/hardware/camera2/CameraProperties.java @@ -16,6 +16,8 @@ package android.hardware.camera2; +import android.hardware.camera2.impl.CameraMetadataNative; + /** * <p>The properties describing a * {@link CameraDevice CameraDevice}.</p> @@ -29,6 +31,21 @@ package android.hardware.camera2; */ public final class CameraProperties extends CameraMetadata { + private final CameraMetadataNative mProperties; + + /** + * Takes ownership of the passed-in properties object + * @hide + */ + public CameraProperties(CameraMetadataNative properties) { + mProperties = properties; + } + + @Override + public <T> T get(Key<T> key) { + return mProperties.get(key); + } + /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * The key entries below this point are generated from metadata * definitions in /system/media/camera/docs. Do not modify by hand or diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index e7c1b54..c3a636d 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -16,6 +16,7 @@ package android.hardware.camera2; +import android.hardware.camera2.impl.CameraMetadataNative; import android.os.Parcel; import android.os.Parcelable; import android.view.Surface; @@ -24,13 +25,16 @@ import java.util.HashSet; /** - * <p>All the settings required to capture a single image from the image sensor.</p> + * <p>An immutable package of settings and outputs needed to capture a single + * image from the camera device.</p> * * <p>Contains the configuration for the capture hardware (sensor, lens, flash), - * the processing pipeline, the control algorithms, and the output buffers.</p> + * the processing pipeline, the control algorithms, and the output buffers. Also + * contains the list of target Surfaces to send image data to for this + * capture.</p> * - * <p>CaptureRequests can be created by calling - * {@link CameraDevice#createCaptureRequest}</p> + * <p>CaptureRequests can be created by using a {@link Builder} instance, + * obtained by calling {@link CameraDevice#createCaptureRequest}</p> * * <p>CaptureRequests are given to {@link CameraDevice#capture} or * {@link CameraDevice#setRepeatingRequest} to capture images from a camera.</p> @@ -38,7 +42,8 @@ import java.util.HashSet; * <p>Each request can specify a different subset of target Surfaces for the * camera to send the captured data to. All the surfaces used in a request must * be part of the surface list given to the last call to - * {@link CameraDevice#configureOutputs}.</p> + * {@link CameraDevice#configureOutputs}, when the request is submitted to the + * camera device.</p> * * <p>For example, a request meant for repeating preview might only include the * Surface for the preview SurfaceView or SurfaceTexture, while a @@ -47,64 +52,43 @@ import java.util.HashSet; * * @see CameraDevice#capture * @see CameraDevice#setRepeatingRequest - * @see CameraDevice#createRequest + * @see CameraDevice#createCaptureRequest */ public final class CaptureRequest extends CameraMetadata implements Parcelable { - private final Object mLock = new Object(); - private final HashSet<Surface> mSurfaceSet = new HashSet<Surface>(); + private final HashSet<Surface> mSurfaceSet; + private final CameraMetadataNative mSettings; + private Object mUserTag; /** + * Construct empty request * @hide */ public CaptureRequest() { + mSettings = new CameraMetadataNative(); + mSurfaceSet = new HashSet<Surface>(); } /** - * <p>Add a surface to the list of targets for this request</p> - * - * <p>The Surface added must be one of the surfaces included in the last - * call to {@link CameraDevice#configureOutputs}.</p> - * - * <p>Adding a target more than once has no effect.</p> - * - * @param outputTarget Surface to use as an output target for this request + * Clone from source capture request */ - public void addTarget(Surface outputTarget) { - synchronized (mLock) { - mSurfaceSet.add(outputTarget); - } + private CaptureRequest(CaptureRequest source) { + mSettings = new CameraMetadataNative(source.mSettings); + mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone(); } /** - * <p>Remove a surface from the list of targets for this request.</p> - * - * <p>Removing a target that is not currently added has no effect.</p> - * - * @param outputTarget Surface to use as an output target for this request + * Take ownership of passed-in settings */ - public void removeTarget(Surface outputTarget) { - synchronized (mLock) { - mSurfaceSet.remove(outputTarget); - } + private CaptureRequest(CameraMetadataNative settings) { + mSettings = settings; + mSurfaceSet = new HashSet<Surface>(); } - /** - * Set a tag for this request. - * - * <p>This tag is not used for anything by the camera device, but can be - * used by an application to easily identify a CaptureRequest when it is - * returned by - * {@link CameraDevice.CaptureListener#onCaptureCompleted CaptureListener.onCaptureCompleted} - * - * @param tag an arbitrary Object to store with this request - * @see #getTag - */ - public void setTag(Object tag) { - synchronized (mLock) { - mUserTag = tag; - } + @Override + public <T> T get(Key<T> key) { + return mSettings.get(key); } /** @@ -118,12 +102,10 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { * * @return the last tag Object set on this request, or {@code null} if * no tag has been set. - * @see #setTag + * @see Builder#setTag */ public Object getTag() { - synchronized (mLock) { - return mUserTag; - } + return mUserTag; } public static final Parcelable.Creator<CaptureRequest> CREATOR = @@ -132,6 +114,7 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { public CaptureRequest createFromParcel(Parcel in) { CaptureRequest request = new CaptureRequest(); request.readFromParcel(in); + return request; } @@ -143,35 +126,152 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { /** * Expand this object from a Parcel. + * Hidden since this breaks the immutability of CaptureRequest, but is + * needed to receive CaptureRequests with aidl. + * * @param in The parcel from which the object should be read + * @hide */ - @Override public void readFromParcel(Parcel in) { - synchronized (mLock) { - super.readFromParcel(in); + mSettings.readFromParcel(in); - mSurfaceSet.clear(); + mSurfaceSet.clear(); - Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader()); + Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader()); - if (parcelableArray == null) { - return; - } + if (parcelableArray == null) { + return; + } - for (Parcelable p : parcelableArray) { - Surface s = (Surface) p; - mSurfaceSet.add(s); - } + for (Parcelable p : parcelableArray) { + Surface s = (Surface) p; + mSurfaceSet.add(s); } } @Override + public int describeContents() { + return 0; + } + + @Override public void writeToParcel(Parcel dest, int flags) { - synchronized (mLock) { - super.writeToParcel(dest, flags); + mSettings.writeToParcel(dest, flags); + dest.writeParcelableArray(mSurfaceSet.toArray(new Surface[mSurfaceSet.size()]), flags); + } + + /** + * A builder for capture requests. + * + * <p>To obtain a builder instance, use the + * {@link CameraDevice#createCaptureRequest} method, which initializes the + * request fields to one of the templates defined in {@link CameraDevice}. + * + * @see CameraDevice#createCaptureRequest + * @see #TEMPLATE_PREVIEW + * @see #TEMPLATE_RECORD + * @see #TEMPLATE_STILL_CAPTURE + * @see #TEMPLATE_VIDEO_SNAPSHOT + * @see #TEMPLATE_MANUAL + */ + public final static class Builder { + + private CaptureRequest mRequest; + + /** + * Initialize the builder using the template; the request takes + * ownership of the template. + * + * @hide + */ + public Builder(CameraMetadataNative template) { + mRequest = new CaptureRequest(template); + } + + /** + * <p>Add a surface to the list of targets for this request</p> + * + * <p>The Surface added must be one of the surfaces included in the most + * recent call to {@link CameraDevice#configureOutputs}, when the + * request is given to the camera device.</p> + * + * <p>Adding a target more than once has no effect.</p> + * + * @param outputTarget Surface to use as an output target for this request + */ + public void addTarget(Surface outputTarget) { + mRequest.mSurfaceSet.add(outputTarget); + } - dest.writeParcelableArray(mSurfaceSet.toArray(new Surface[mSurfaceSet.size()]), flags); + /** + * <p>Remove a surface from the list of targets for this request.</p> + * + * <p>Removing a target that is not currently added has no effect.</p> + * + * @param outputTarget Surface to use as an output target for this request + */ + public void removeTarget(Surface outputTarget) { + mRequest.mSurfaceSet.remove(outputTarget); } + + /** + * Set a capture request field to a value. The field definitions can be + * found in {@link CaptureRequest}. + * + * @param key The metadata field to write. + * @param value The value to set the field to, which must be of a matching + * type to the key. + */ + public <T> void set(Key<T> key, T value) { + mRequest.mSettings.set(key, value); + } + + /** + * Get a capture request field value. The field definitions can be + * found in {@link CaptureRequest}. + * + * @throws IllegalArgumentException if the key was not valid + * + * @param key The metadata field to read. + * @return The value of that key, or {@code null} if the field is not set. + */ + public <T> T get(Key<T> key) { + return mRequest.mSettings.get(key); + } + + /** + * Set a tag for this request. + * + * <p>This tag is not used for anything by the camera device, but can be + * used by an application to easily identify a CaptureRequest when it is + * returned by + * {@link CameraDevice.CaptureListener#onCaptureCompleted CaptureListener.onCaptureCompleted} + * + * @param tag an arbitrary Object to store with this request + * @see CaptureRequest#getTag + */ + public void setTag(Object tag) { + mRequest.mUserTag = tag; + } + + /** + * Build a request using the current target Surfaces and settings. + * + * @return A new capture request instance, ready for submission to the + * camera device. + */ + public CaptureRequest build() { + return new CaptureRequest(mRequest); + } + + + /** + * @hide + */ + public boolean isEmpty() { + return mRequest.mSettings.isEmpty(); + } + } /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index bd151a2..f83dad7 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -18,6 +18,7 @@ package android.hardware.camera2; import android.graphics.Point; import android.graphics.Rect; +import android.hardware.camera2.impl.CameraMetadataNative; /** * <p>The results of a single image capture from the image sensor.</p> @@ -34,10 +35,20 @@ import android.graphics.Rect; * */ public final class CaptureResult extends CameraMetadata { + + private final CameraMetadataNative mResults; + /** + * Takes ownership of the passed-in properties object * @hide */ - public CaptureResult() { + public CaptureResult(CameraMetadataNative results) { + mResults = results; + } + + @Override + public <T> T get(Key<T> key) { + return mResults.get(key); } /** diff --git a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl index 4172238..4054a92 100644 --- a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl +++ b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl @@ -16,7 +16,7 @@ package android.hardware.camera2; -import android.hardware.camera2.CameraMetadata; +import android.hardware.camera2.impl.CameraMetadataNative; /** @hide */ interface ICameraDeviceCallbacks @@ -26,5 +26,5 @@ interface ICameraDeviceCallbacks */ oneway void notifyCallback(int msgType, int ext1, int ext2); - oneway void onResultReceived(int frameId, in CameraMetadata result); + oneway void onResultReceived(int frameId, in CameraMetadataNative result); } diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl index b1724de..1936963 100644 --- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl +++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl @@ -17,7 +17,7 @@ package android.hardware.camera2; import android.view.Surface; -import android.hardware.camera2.CameraMetadata; +import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.CaptureRequest; /** @hide */ @@ -40,9 +40,9 @@ interface ICameraDeviceUser // non-negative value is the stream ID. negative value is status_t int createStream(int width, int height, int format, in Surface surface); - int createDefaultRequest(int templateId, out CameraMetadata request); + int createDefaultRequest(int templateId, out CameraMetadataNative request); - int getCameraInfo(out CameraMetadata info); + int getCameraInfo(out CameraMetadataNative info); int waitUntilIdle(); diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java index 966fcfa..995555a 100644 --- a/core/java/android/hardware/camera2/impl/CameraDevice.java +++ b/core/java/android/hardware/camera2/impl/CameraDevice.java @@ -42,7 +42,7 @@ import java.util.List; import java.util.Stack; /** - * HAL2.1+ implementation of CameraDevice Use CameraManager#open to instantiate + * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate */ public class CameraDevice implements android.hardware.camera2.CameraDevice { @@ -90,8 +90,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { @Override public CameraProperties getProperties() throws CameraAccessException { - CameraProperties properties = new CameraProperties(); - CameraMetadata info = new CameraMetadata(); + CameraMetadataNative info = new CameraMetadataNative(); try { mRemoteDevice.getCameraInfo(/*out*/info); @@ -102,7 +101,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { return null; } - properties.swap(info); + CameraProperties properties = new CameraProperties(info); return properties; } @@ -157,11 +156,11 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } @Override - public CaptureRequest createCaptureRequest(int templateType) throws CameraAccessException { - + public CaptureRequest.Builder createCaptureRequest(int templateType) + throws CameraAccessException { synchronized (mLock) { - CameraMetadata templatedRequest = new CameraMetadata(); + CameraMetadataNative templatedRequest = new CameraMetadataNative(); try { mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest); @@ -172,11 +171,10 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { return null; } - CaptureRequest request = new CaptureRequest(); - request.swap(templatedRequest); - - return request; + CaptureRequest.Builder builder = + new CaptureRequest.Builder(templatedRequest); + return builder; } } @@ -404,7 +402,8 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } @Override - public void onResultReceived(int requestId, CameraMetadata result) throws RemoteException { + public void onResultReceived(int requestId, CameraMetadataNative result) + throws RemoteException { if (DEBUG) { Log.d(TAG, "Received result for id " + requestId); } @@ -432,8 +431,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { return; } - final CaptureResult resultAsCapture = new CaptureResult(); - resultAsCapture.swap(result); + final CaptureResult resultAsCapture = new CaptureResult(result); holder.getHandler().post( new Runnable() { diff --git a/core/java/android/hardware/camera2/CameraMetadata.aidl b/core/java/android/hardware/camera2/impl/CameraMetadataNative.aidl index 71dd471..4a89129 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.aidl +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.aidl @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.hardware.camera2; +package android.hardware.camera2.impl; /** @hide */ -parcelable CameraMetadata; +parcelable CameraMetadataNative; diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java new file mode 100644 index 0000000..020d7b6 --- /dev/null +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -0,0 +1,669 @@ +/* + * 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.CameraMetadata; +import android.hardware.camera2.Rational; +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; +import java.util.Map; + +/** + * Implementation of camera metadata marshal/unmarshal across Binder to + * the camera service + */ +public class CameraMetadataNative extends CameraMetadata implements Parcelable { + + private static final String TAG = "CameraMetadataJV"; + + public CameraMetadataNative() { + super(); + mMetadataPtr = nativeAllocate(); + if (mMetadataPtr == 0) { + throw new OutOfMemoryError("Failed to allocate native CameraMetadata"); + } + } + + /** + * Copy constructor - clone metadata + */ + public CameraMetadataNative(CameraMetadataNative other) { + super(); + mMetadataPtr = nativeAllocateCopy(other); + if (mMetadataPtr == 0) { + throw new OutOfMemoryError("Failed to allocate native CameraMetadata"); + } + } + + public static final Parcelable.Creator<CameraMetadataNative> CREATOR = + new Parcelable.Creator<CameraMetadataNative>() { + @Override + public CameraMetadataNative createFromParcel(Parcel in) { + CameraMetadataNative metadata = new CameraMetadataNative(); + metadata.readFromParcel(in); + return metadata; + } + + @Override + public CameraMetadataNative[] newArray(int size) { + return new CameraMetadataNative[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + nativeWriteToParcel(dest); + } + + @SuppressWarnings("unchecked") + @Override + public <T> T get(Key<T> key) { + int tag = key.getTag(); + byte[] values = readValues(tag); + if (values == null) { + return null; + } + + int nativeType = getNativeType(tag); + + ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); + return unpackSingle(buffer, key.getType(), nativeType); + } + + public void readFromParcel(Parcel in) { + nativeReadFromParcel(in); + } + + /** + * Set a camera metadata field to a value. The field definitions can be + * found in {@link CameraProperties}, {@link CaptureResult}, and + * {@link CaptureRequest}. + * + * @param key The metadata field to write. + * @param value The value to set the field to, which must be of a matching + * type to the key. + */ + public <T> void set(Key<T> key, T value) { + int tag = key.getTag(); + + if (value == null) { + writeValues(tag, null); + return; + } + + int nativeType = getNativeType(tag); + + int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true); + + // 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); + + writeValues(tag, values); + } + + // Keep up-to-date with camera_metadata.h + /** + * @hide + */ + public static final int TYPE_BYTE = 0; + /** + * @hide + */ + public static final int TYPE_INT32 = 1; + /** + * @hide + */ + public static final int TYPE_FLOAT = 2; + /** + * @hide + */ + public static final int TYPE_INT64 = 3; + /** + * @hide + */ + public static final int TYPE_DOUBLE = 4; + /** + * @hide + */ + public static final int TYPE_RATIONAL = 5; + /** + * @hide + */ + public static final int NUM_TYPES = 6; + + private void close() { + // this sets mMetadataPtr to 0 + nativeClose(); + 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; + + 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 long mMetadataPtr; // native CameraMetadata* + + private native long nativeAllocate(); + private native long nativeAllocateCopy(CameraMetadataNative other) + throws NullPointerException; + + private native synchronized void nativeWriteToParcel(Parcel dest); + private native synchronized void nativeReadFromParcel(Parcel source); + private native synchronized void nativeSwap(CameraMetadataNative other) + throws NullPointerException; + private native synchronized void nativeClose(); + private native synchronized boolean nativeIsEmpty(); + private native synchronized int nativeGetEntryCount(); + + private native synchronized byte[] nativeReadValues(int tag); + private native synchronized void nativeWriteValues(int tag, byte[] src); + + private static native int nativeGetTagFromKey(String keyName) + throws IllegalArgumentException; + private static native int nativeGetTypeFromTag(int tag) + throws IllegalArgumentException; + private static native void nativeClassInit(); + + /** + * <p>Perform a 0-copy swap of the internal metadata with another object.</p> + * + * <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p> + * + * @param other Metadata to swap with + * @throws NullPointerException if other was null + * @hide + */ + public void swap(CameraMetadataNative other) { + nativeSwap(other); + } + + /** + * @hide + */ + public int getEntryCount() { + return nativeGetEntryCount(); + } + + /** + * Does this metadata contain at least 1 entry? + * + * @hide + */ + public boolean isEmpty() { + return nativeIsEmpty(); + } + + /** + * Convert a key string into the equivalent native tag. + * + * @throws IllegalArgumentException if the key was not recognized + * @throws NullPointerException if the key was null + * + * @hide + */ + public static int getTag(String key) { + return nativeGetTagFromKey(key); + } + + /** + * Get the underlying native type for a tag. + * + * @param tag An integer tag, see e.g. {@link #getTag} + * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE} + * + * @hide + */ + public static int getNativeType(int tag) { + return nativeGetTypeFromTag(tag); + } + + /** + * <p>Updates the existing entry for tag with the new bytes pointed by src, erasing + * the entry if src was null.</p> + * + * <p>An empty array can be passed in to update the entry to 0 elements.</p> + * + * @param tag An integer tag, see e.g. {@link #getTag} + * @param src An array of bytes, or null to erase the entry + * + * @hide + */ + public void writeValues(int tag, byte[] src) { + nativeWriteValues(tag, src); + } + + /** + * <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize + * the data properly.</p> + * + * <p>An empty array can be returned to denote an existing entry with 0 elements.</p> + * + * @param tag An integer tag, see e.g. {@link #getTag} + * + * @return {@code null} if there were 0 entries for this tag, a byte[] otherwise. + * @hide + */ + public byte[] readValues(int tag) { + // TODO: Optimization. Native code returns a ByteBuffer instead. + return nativeReadValues(tag); + } + + @Override + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + 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. + * + * @param enumType The class for an enum + * @param values A list of values mapping to the ordinals of the enum + * + * @hide + */ + 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); + } + + 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; + } + + /** + * 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]; + } + + 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); + } + + return marshaler; + } + + /** + * We use a class initializer to allow the native code to cache some field offsets + */ + static { + System.loadLibrary("media_jni"); + nativeClassInit(); + + Log.v(TAG, "Shall register metadata marshalers"); + + // load built-in marshallers + registerMarshaler(new MetadataMarshalRect()); + registerMarshaler(new MetadataMarshalSize()); + registerMarshaler(new MetadataMarshalString()); + + Log.v(TAG, "Registered metadata marshalers"); + } + +} diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java b/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java index a934d75..6d224ef 100644 --- a/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java +++ b/core/java/android/hardware/camera2/impl/MetadataMarshalClass.java @@ -26,7 +26,7 @@ public interface MetadataMarshalClass<T> { * @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.CameraMetadata#TYPE_BYTE TYPE_BYTE}. + * {@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 @@ -37,7 +37,7 @@ public interface MetadataMarshalClass<T> { * 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.CameraMetadata#TYPE_BYTE TYPE_BYTE}. + * {@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 */ @@ -50,7 +50,7 @@ public interface MetadataMarshalClass<T> { * will are likely to only support one type. * * @param nativeType the native type, e.g. - * {@link android.hardware.camera2.CameraMetadata#TYPE_BYTE TYPE_BYTE} + * {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE} * @return true if it supports, false otherwise */ boolean isNativeTypeSupported(int nativeType); @@ -60,7 +60,7 @@ public interface MetadataMarshalClass<T> { /** * How many bytes T will take up if marshalled to/from nativeType * @param nativeType the native type, e.g. - * {@link android.hardware.camera2.CameraMetadata#TYPE_BYTE TYPE_BYTE} + * {@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 index 384223c..ab72c4f 100644 --- a/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java +++ b/core/java/android/hardware/camera2/impl/MetadataMarshalRect.java @@ -16,7 +16,6 @@ package android.hardware.camera2.impl; import android.graphics.Rect; -import android.hardware.camera2.CameraMetadata; import java.nio.ByteBuffer; @@ -58,7 +57,7 @@ public class MetadataMarshalRect implements MetadataMarshalClass<Rect> { @Override public boolean isNativeTypeSupported(int nativeType) { - return nativeType == CameraMetadata.TYPE_INT32; + return nativeType == CameraMetadataNative.TYPE_INT32; } @Override diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java b/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java index 793bba7..e8143e0 100644 --- a/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java +++ b/core/java/android/hardware/camera2/impl/MetadataMarshalSize.java @@ -15,7 +15,6 @@ */ package android.hardware.camera2.impl; -import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.Size; import java.nio.ByteBuffer; @@ -51,7 +50,7 @@ public class MetadataMarshalSize implements MetadataMarshalClass<Size> { @Override public boolean isNativeTypeSupported(int nativeType) { - return nativeType == CameraMetadata.TYPE_INT32; + return nativeType == CameraMetadataNative.TYPE_INT32; } @Override diff --git a/core/java/android/hardware/camera2/impl/MetadataMarshalString.java b/core/java/android/hardware/camera2/impl/MetadataMarshalString.java index 50b3347..b61b8d3 100644 --- a/core/java/android/hardware/camera2/impl/MetadataMarshalString.java +++ b/core/java/android/hardware/camera2/impl/MetadataMarshalString.java @@ -15,8 +15,6 @@ */ package android.hardware.camera2.impl; -import android.hardware.camera2.CameraMetadata; - import java.nio.ByteBuffer; import java.nio.charset.Charset; @@ -71,7 +69,7 @@ public class MetadataMarshalString implements MetadataMarshalClass<String> { @Override public boolean isNativeTypeSupported(int nativeType) { - return nativeType == CameraMetadata.TYPE_BYTE; + return nativeType == CameraMetadataNative.TYPE_BYTE; } @Override diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp index 852c4d4..3c7da1e 100644 --- a/core/jni/android_hardware_camera2_CameraMetadata.cpp +++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp @@ -38,7 +38,7 @@ #endif // fully-qualified class name -#define CAMERA_METADATA_CLASS_NAME "android/hardware/camera2/CameraMetadata" +#define CAMERA_METADATA_CLASS_NAME "android/hardware/camera2/impl/CameraMetadataNative" using namespace android; @@ -152,6 +152,21 @@ static jlong CameraMetadata_allocate(JNIEnv *env, jobject thiz) { return reinterpret_cast<jlong>(new CameraMetadata()); } +static jlong CameraMetadata_allocateCopy(JNIEnv *env, jobject thiz, + jobject other) { + ALOGV("%s", __FUNCTION__); + + CameraMetadata* otherMetadata = + CameraMetadata_getPointerThrow(env, other, "other"); + + // In case of exception, return + if (otherMetadata == NULL) return NULL; + + // Clone native metadata and return new pointer + return reinterpret_cast<jlong>(new CameraMetadata(*otherMetadata)); +} + + static jboolean CameraMetadata_isEmpty(JNIEnv *env, jobject thiz) { ALOGV("%s", __FUNCTION__); @@ -361,6 +376,9 @@ static JNINativeMethod gCameraMetadataMethods[] = { { "nativeAllocate", "()J", (void*)CameraMetadata_allocate }, + { "nativeAllocateCopy", + "(L" CAMERA_METADATA_CLASS_NAME ";)J", + (void *)CameraMetadata_allocateCopy }, { "nativeIsEmpty", "()Z", (void*)CameraMetadata_isEmpty }, diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java index 624bbaa..1b7faec 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java @@ -25,6 +25,7 @@ import android.hardware.IProCameraUser; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.ICameraDeviceCallbacks; import android.hardware.camera2.ICameraDeviceUser; +import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.utils.BinderHolder; import android.hardware.camera2.utils.CameraBinderDecorator; import android.os.Binder; @@ -155,7 +156,7 @@ public class CameraBinderTest extends AndroidTestCase { } @Override - public void onResultReceived(int frameId, CameraMetadata result) throws RemoteException { + public void onResultReceived(int frameId, CameraMetadataNative result) throws RemoteException { } } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java index 2f271bb..56d73c0 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java @@ -22,6 +22,7 @@ import android.hardware.camera2.CameraProperties; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.ICameraDeviceCallbacks; import android.hardware.camera2.ICameraDeviceUser; +import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.utils.BinderHolder; import android.os.RemoteException; import android.test.AndroidTestCase; @@ -62,13 +63,13 @@ public class CameraDeviceBinderTest extends AndroidTestCase { } @Override - public void onResultReceived(int frameId, CameraMetadata result) throws RemoteException { + public void onResultReceived(int frameId, CameraMetadataNative result) throws RemoteException { } } - class IsMetadataNotEmpty extends ArgumentMatcher<CameraMetadata> { + class IsMetadataNotEmpty extends ArgumentMatcher<CameraMetadataNative> { public boolean matches(Object obj) { - return !((CameraMetadata) obj).isEmpty(); + return !((CameraMetadataNative) obj).isEmpty(); } } @@ -78,20 +79,17 @@ public class CameraDeviceBinderTest extends AndroidTestCase { mSurface = new Surface(mSurfaceTexture); } - private CaptureRequest createDefaultRequest(boolean needStream) throws Exception { - CameraMetadata metadata = new CameraMetadata(); + private CaptureRequest.Builder createDefaultBuilder(boolean needStream) throws Exception { + CameraMetadataNative metadata = new CameraMetadataNative(); assertTrue(metadata.isEmpty()); - CaptureRequest request = new CaptureRequest(); - assertTrue(request.isEmpty()); - int status = mCameraUser.createDefaultRequest(TEMPLATE_PREVIEW, /* out */metadata); assertEquals(CameraBinderTestUtils.NO_ERROR, status); assertFalse(metadata.isEmpty()); - request.swap(metadata); + CaptureRequest.Builder request = new CaptureRequest.Builder(metadata); assertFalse(request.isEmpty()); - assertTrue(metadata.isEmpty()); + assertFalse(metadata.isEmpty()); if (needStream) { int streamId = mCameraUser.createStream(/* ignored */10, /* ignored */20, /* ignored */30, mSurface); @@ -150,14 +148,13 @@ public class CameraDeviceBinderTest extends AndroidTestCase { @SmallTest public void testCreateDefaultRequest() throws Exception { - CameraMetadata metadata = new CameraMetadata(); + CameraMetadataNative metadata = new CameraMetadataNative(); assertTrue(metadata.isEmpty()); int status = mCameraUser.createDefaultRequest(TEMPLATE_PREVIEW, /* out */metadata); assertEquals(CameraBinderTestUtils.NO_ERROR, status); assertFalse(metadata.isEmpty()); - metadata.close(); } @SmallTest @@ -208,37 +205,39 @@ public class CameraDeviceBinderTest extends AndroidTestCase { @SmallTest public void testSubmitBadRequest() throws Exception { - CaptureRequest request = createDefaultRequest(/* needStream */false); - int status = mCameraUser.submitRequest(request, /* streaming */false); + CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */false); + CaptureRequest request1 = builder.build(); + int status = mCameraUser.submitRequest(request1, /* streaming */false); assertEquals("Expected submitRequest to return BAD_VALUE " + "since we had 0 surface targets set.", CameraBinderTestUtils.BAD_VALUE, status); - request.addTarget(mSurface); - status = mCameraUser.submitRequest(request, /* streaming */false); + builder.addTarget(mSurface); + CaptureRequest request2 = builder.build(); + status = mCameraUser.submitRequest(request2, /* streaming */false); assertEquals("Expected submitRequest to return BAD_VALUE since " + "the target surface wasn't registered with createStream.", CameraBinderTestUtils.BAD_VALUE, status); - - request.close(); } @SmallTest public void testSubmitGoodRequest() throws Exception { - CaptureRequest request = createDefaultRequest(/* needStream */true); + CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */true); + CaptureRequest request = builder.build(); // Submit valid request twice. int requestId1 = submitCameraRequest(request, /* streaming */false); int requestId2 = submitCameraRequest(request, /* streaming */false); assertNotSame("Request IDs should be unique for multiple requests", requestId1, requestId2); - request.close(); } @SmallTest public void testSubmitStreamingRequest() throws Exception { - CaptureRequest request = createDefaultRequest(/* needStream */true); + CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */true); + + CaptureRequest request = builder.build(); // Submit valid request once (non-streaming), and another time // (streaming) @@ -260,12 +259,11 @@ public class CameraDeviceBinderTest extends AndroidTestCase { assertEquals("Streaming request IDs should be cancellable", CameraBinderTestUtils.NO_ERROR, status); - request.close(); } @SmallTest public void testCameraInfo() throws RemoteException { - CameraMetadata info = new CameraMetadata(); + CameraMetadataNative info = new CameraMetadataNative(); int status = mCameraUser.getCameraInfo(/*out*/info); assertEquals(CameraBinderTestUtils.NO_ERROR, status); @@ -276,8 +274,8 @@ public class CameraDeviceBinderTest extends AndroidTestCase { @SmallTest public void testWaitUntilIdle() throws Exception { - CaptureRequest request = createDefaultRequest(/* needStream */true); - int requestIdStreaming = submitCameraRequest(request, /* streaming */true); + CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */true); + int requestIdStreaming = submitCameraRequest(builder.build(), /* streaming */true); // Test Bad case first: waitUntilIdle when there is active repeating request int status = mCameraUser.waitUntilIdle(); @@ -294,7 +292,7 @@ public class CameraDeviceBinderTest extends AndroidTestCase { @SmallTest public void testCaptureResultCallbacks() throws Exception { IsMetadataNotEmpty matcher = new IsMetadataNotEmpty(); - CaptureRequest request = createDefaultRequest(/* needStream */true); + CaptureRequest request = createDefaultBuilder(/* needStream */true).build(); // Test both single request and streaming request. int requestId1 = submitCameraRequest(request, /* streaming */false); @@ -307,7 +305,6 @@ public class CameraDeviceBinderTest extends AndroidTestCase { .onResultReceived( eq(streamingId), argThat(matcher)); - request.close(); } @SmallTest @@ -319,7 +316,7 @@ public class CameraDeviceBinderTest extends AndroidTestCase { assertEquals(CameraBinderTestUtils.NO_ERROR, status); // Then set up a stream - CaptureRequest request = createDefaultRequest(/* needStream */true); + CaptureRequest request = createDefaultBuilder(/* needStream */true).build(); // Flush should still be a no-op, really status = mCameraUser.flush(); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java index ecf01d9..874e078 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java @@ -23,8 +23,9 @@ import android.graphics.Rect; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.Rational; import android.hardware.camera2.Size; +import android.hardware.camera2.impl.CameraMetadataNative; -import static android.hardware.camera2.CameraMetadata.*; +import static android.hardware.camera2.impl.CameraMetadataNative.*; import java.lang.reflect.Array; import java.nio.ByteBuffer; @@ -42,7 +43,7 @@ import static org.junit.Assert.assertArrayEquals; */ public class CameraMetadataTest extends junit.framework.TestCase { - CameraMetadata mMetadata; + CameraMetadataNative mMetadata; Parcel mParcel; // Sections @@ -62,13 +63,12 @@ public class CameraMetadataTest extends junit.framework.TestCase { @Override public void setUp() { - mMetadata = new CameraMetadata(); + mMetadata = new CameraMetadataNative(); mParcel = Parcel.obtain(); } @Override public void tearDown() throws Exception { - mMetadata.close(); mMetadata = null; mParcel.recycle(); @@ -82,115 +82,47 @@ public class CameraMetadataTest extends junit.framework.TestCase { } @SmallTest - public void testClose() throws Exception { - mMetadata.isEmpty(); // no throw - - assertFalse(mMetadata.isClosed()); - - mMetadata.close(); - - assertTrue(mMetadata.isClosed()); - - // OK: second close should not throw - mMetadata.close(); - - assertTrue(mMetadata.isClosed()); - - // All other calls after close should throw IllegalStateException - - try { - mMetadata.isEmpty(); - fail("Unreachable -- isEmpty after close should throw IllegalStateException"); - } catch (IllegalStateException e) { - // good: we expect calling this method after close to fail - } - - try { - mMetadata.getEntryCount(); - fail("Unreachable -- getEntryCount after close should throw IllegalStateException"); - } catch (IllegalStateException e) { - // good: we expect calling this method after close to fail - } - - - try { - mMetadata.swap(mMetadata); - fail("Unreachable -- swap after close should throw IllegalStateException"); - } catch (IllegalStateException e) { - // good: we expect calling this method after close to fail - } - - try { - mMetadata.readFromParcel(mParcel); - fail("Unreachable -- readFromParcel after close should throw IllegalStateException"); - } catch (IllegalStateException e) { - // good: we expect calling this method after close to fail - } - - try { - mMetadata.writeToParcel(mParcel, /*flags*/0); - fail("Unreachable -- writeToParcel after close should throw IllegalStateException"); - } catch (IllegalStateException e) { - // good: we expect calling this method after close to fail - } - - try { - mMetadata.readValues(/*tag*/0); - fail("Unreachable -- readValues after close should throw IllegalStateException"); - } catch (IllegalStateException e) { - // good: we expect calling this method after close to fail - } - - try { - mMetadata.writeValues(/*tag*/0, /*source*/new byte[] { 1,2,3 }); - fail("Unreachable -- readValues after close should throw IllegalStateException"); - } catch (IllegalStateException e) { - // good: we expect calling this method after close to fail - } - } - - @SmallTest public void testGetTagFromKey() { // Test success assertEquals(ANDROID_COLOR_CORRECTION_MODE, - CameraMetadata.getTag("android.colorCorrection.mode")); + CameraMetadataNative.getTag("android.colorCorrection.mode")); assertEquals(ANDROID_COLOR_CORRECTION_TRANSFORM, - CameraMetadata.getTag("android.colorCorrection.transform")); + CameraMetadataNative.getTag("android.colorCorrection.transform")); assertEquals(ANDROID_CONTROL_AE_ANTIBANDING_MODE, - CameraMetadata.getTag("android.control.aeAntibandingMode")); + CameraMetadataNative.getTag("android.control.aeAntibandingMode")); assertEquals(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, - CameraMetadata.getTag("android.control.aeExposureCompensation")); + CameraMetadataNative.getTag("android.control.aeExposureCompensation")); // Test failures try { - CameraMetadata.getTag(null); + CameraMetadataNative.getTag(null); fail("A null key should throw NPE"); } catch(NullPointerException e) { } try { - CameraMetadata.getTag("android.control"); + CameraMetadataNative.getTag("android.control"); fail("A section name only should not be a valid key"); } catch(IllegalArgumentException e) { } try { - CameraMetadata.getTag("android.control.thisTagNameIsFakeAndDoesNotExist"); + CameraMetadataNative.getTag("android.control.thisTagNameIsFakeAndDoesNotExist"); fail("A valid section with an invalid tag name should not be a valid key"); } catch(IllegalArgumentException e) { } try { - CameraMetadata.getTag("android"); + CameraMetadataNative.getTag("android"); fail("A namespace name only should not be a valid key"); } catch(IllegalArgumentException e) { } try { - CameraMetadata.getTag("this.key.is.definitely.invalid"); + CameraMetadataNative.getTag("this.key.is.definitely.invalid"); fail("A completely fake key name should not be valid"); } catch(IllegalArgumentException e) { } @@ -198,14 +130,14 @@ public class CameraMetadataTest extends junit.framework.TestCase { @SmallTest public void testGetTypeFromTag() { - assertEquals(TYPE_BYTE, CameraMetadata.getNativeType(ANDROID_COLOR_CORRECTION_MODE)); - assertEquals(TYPE_FLOAT, CameraMetadata.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM)); - assertEquals(TYPE_BYTE, CameraMetadata.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE)); + assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE)); + assertEquals(TYPE_FLOAT, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM)); + assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE)); assertEquals(TYPE_INT32, - CameraMetadata.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION)); + CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION)); try { - CameraMetadata.getNativeType(0xDEADF00D); + CameraMetadataNative.getNativeType(0xDEADF00D); fail("No type should exist for invalid tag 0xDEADF00D"); } catch(IllegalArgumentException e) { } @@ -454,7 +386,7 @@ public class CameraMetadataTest extends junit.framework.TestCase { @SmallTest public void testReadWriteEnumWithCustomValues() { - CameraMetadata.registerEnumValues(AeAntibandingMode.class, new int[] { + CameraMetadataNative.registerEnumValues(AeAntibandingMode.class, new int[] { 0, 10, 20, @@ -475,7 +407,7 @@ public class CameraMetadataTest extends junit.framework.TestCase { Key<AeAntibandingMode[]> aeAntibandingModeKey = new Key<AeAntibandingMode[]>("android.control.aeAvailableAntibandingModes", AeAntibandingMode[].class); - byte[] aeAntibandingModeValues = mMetadata.readValues(CameraMetadata + byte[] aeAntibandingModeValues = mMetadata.readValues(CameraMetadataNative .getTag("android.control.aeAvailableAntibandingModes")); byte[] expectedValues = new byte[] { 0, 10, 20, 30 }; assertArrayEquals(expectedValues, aeAntibandingModeValues); @@ -485,7 +417,7 @@ public class CameraMetadataTest extends junit.framework.TestCase { * Stranger cases that don't use byte enums */ // int (n) - CameraMetadata.registerEnumValues(AvailableFormat.class, new int[] { + CameraMetadataNative.registerEnumValues(AvailableFormat.class, new int[] { 0x20, 0x32315659, 0x11, @@ -505,7 +437,7 @@ public class CameraMetadataTest extends junit.framework.TestCase { Key<AeAntibandingMode> availableFormatsKey = new Key<AeAntibandingMode>("android.scaler.availableFormats", AeAntibandingMode.class); - byte[] availableFormatValues = mMetadata.readValues(CameraMetadata + byte[] availableFormatValues = mMetadata.readValues(CameraMetadataNative .getTag(availableFormatsKey.getName())); int[] expectedIntValues = new int[] { |