| /* |
| * 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 DEBUG = false; |
| |
| 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 (DEBUG) { |
| 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 (DEBUG) { |
| 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]; |
| } |
| } |