1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.hardware.camera2.marshal.impl; 17 18 import android.hardware.camera2.marshal.Marshaler; 19 import android.hardware.camera2.marshal.MarshalQueryable; 20 import android.hardware.camera2.utils.TypeReference; 21 import android.util.Log; 22 23 import java.nio.ByteBuffer; 24 import java.util.HashMap; 25 26 import static android.hardware.camera2.impl.CameraMetadataNative.*; 27 import static android.hardware.camera2.marshal.MarshalHelpers.*; 28 29 /** 30 * Marshal any simple enum (0-arg constructors only) into/from either 31 * {@code TYPE_BYTE} or {@code TYPE_INT32}. 32 * 33 * <p>Default values of the enum are mapped to its ordinal; this can be overridden 34 * by providing a manual value with {@link #registerEnumValues}.</p> 35 36 * @param <T> the type of {@code Enum} 37 */ 38 public class MarshalQueryableEnum<T extends Enum<T>> implements MarshalQueryable<T> { 39 40 private static final String TAG = MarshalQueryableEnum.class.getSimpleName(); 41 private static final boolean DEBUG = false; 42 43 private static final int UINT8_MIN = 0x0; 44 private static final int UINT8_MAX = (1 << Byte.SIZE) - 1; 45 private static final int UINT8_MASK = UINT8_MAX; 46 47 private class MarshalerEnum extends Marshaler<T> { 48 49 private final Class<T> mClass; 50 51 @SuppressWarnings("unchecked") MarshalerEnum(TypeReference<T> typeReference, int nativeType)52 protected MarshalerEnum(TypeReference<T> typeReference, int nativeType) { 53 super(MarshalQueryableEnum.this, typeReference, nativeType); 54 55 mClass = (Class<T>)typeReference.getRawType(); 56 } 57 58 @Override marshal(T value, ByteBuffer buffer)59 public void marshal(T value, ByteBuffer buffer) { 60 int enumValue = getEnumValue(value); 61 62 if (mNativeType == TYPE_INT32) { 63 buffer.putInt(enumValue); 64 } else if (mNativeType == TYPE_BYTE) { 65 if (enumValue < UINT8_MIN || enumValue > UINT8_MAX) { 66 throw new UnsupportedOperationException(String.format( 67 "Enum value %x too large to fit into unsigned byte", enumValue)); 68 } 69 buffer.put((byte)enumValue); 70 } else { 71 throw new AssertionError(); 72 } 73 } 74 75 @Override unmarshal(ByteBuffer buffer)76 public T unmarshal(ByteBuffer buffer) { 77 int enumValue; 78 79 switch (mNativeType) { 80 case TYPE_INT32: 81 enumValue = buffer.getInt(); 82 break; 83 case TYPE_BYTE: 84 // get the unsigned byte value; avoid sign extension 85 enumValue = buffer.get() & UINT8_MASK; 86 break; 87 default: 88 throw new AssertionError( 89 "Unexpected native type; impossible since its not supported"); 90 } 91 92 return getEnumFromValue(mClass, enumValue); 93 } 94 95 @Override getNativeSize()96 public int getNativeSize() { 97 return getPrimitiveTypeSize(mNativeType); 98 } 99 } 100 101 @Override createMarshaler(TypeReference<T> managedType, int nativeType)102 public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) { 103 return new MarshalerEnum(managedType, nativeType); 104 } 105 106 @Override isTypeMappingSupported(TypeReference<T> managedType, int nativeType)107 public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) { 108 if (nativeType == TYPE_INT32 || nativeType == TYPE_BYTE) { 109 if (managedType.getType() instanceof Class<?>) { 110 Class<?> typeClass = (Class<?>)managedType.getType(); 111 112 if (typeClass.isEnum()) { 113 if (DEBUG) { 114 Log.v(TAG, "possible enum detected for " + typeClass); 115 } 116 117 // The enum must not take extra arguments 118 try { 119 // match a class like: "public enum Fruits { Apple, Orange; }" 120 typeClass.getDeclaredConstructor(String.class, int.class); 121 return true; 122 } catch (NoSuchMethodException e) { 123 // Skip: custom enum with a special constructor e.g. Foo(T), but need Foo() 124 Log.e(TAG, "Can't marshal class " + typeClass + "; no default constructor"); 125 } catch (SecurityException e) { 126 // Skip: wouldn't be able to touch the enum anyway 127 Log.e(TAG, "Can't marshal class " + typeClass + "; not accessible"); 128 } 129 } 130 } 131 } 132 133 return false; 134 } 135 136 @SuppressWarnings("rawtypes") 137 private static final HashMap<Class<? extends Enum>, int[]> sEnumValues = 138 new HashMap<Class<? extends Enum>, int[]>(); 139 140 /** 141 * Register a non-sequential set of values to be used with the marshal/unmarshal functions. 142 * 143 * <p>This enables get/set to correctly marshal the enum into a value that is C-compatible.</p> 144 * 145 * @param enumType The class for an enum 146 * @param values A list of values mapping to the ordinals of the enum 147 */ registerEnumValues(Class<T> enumType, int[] values)148 public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) { 149 if (enumType.getEnumConstants().length != values.length) { 150 throw new IllegalArgumentException( 151 "Expected values array to be the same size as the enumTypes values " 152 + values.length + " for type " + enumType); 153 } 154 if (DEBUG) { 155 Log.v(TAG, "Registered enum values for type " + enumType + " values"); 156 } 157 158 sEnumValues.put(enumType, values); 159 } 160 161 /** 162 * Get the numeric value from an enum. 163 * 164 * <p>This is usually the same as the ordinal value for 165 * enums that have fully sequential values, although for C-style enums the range of values 166 * may not map 1:1.</p> 167 * 168 * @param enumValue Enum instance 169 * @return Int guaranteed to be ABI-compatible with the C enum equivalent 170 */ getEnumValue(T enumValue)171 private static <T extends Enum<T>> int getEnumValue(T enumValue) { 172 int[] values; 173 values = sEnumValues.get(enumValue.getClass()); 174 175 int ordinal = enumValue.ordinal(); 176 if (values != null) { 177 return values[ordinal]; 178 } 179 180 return ordinal; 181 } 182 183 /** 184 * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method. 185 * 186 * @param enumType Class of the enum we want to find 187 * @param value The numeric value of the enum 188 * @return An instance of the enum 189 */ getEnumFromValue(Class<T> enumType, int value)190 private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) { 191 int ordinal; 192 193 int[] registeredValues = sEnumValues.get(enumType); 194 if (registeredValues != null) { 195 ordinal = -1; 196 197 for (int i = 0; i < registeredValues.length; ++i) { 198 if (registeredValues[i] == value) { 199 ordinal = i; 200 break; 201 } 202 } 203 } else { 204 ordinal = value; 205 } 206 207 T[] values = enumType.getEnumConstants(); 208 209 if (ordinal < 0 || ordinal >= values.length) { 210 throw new IllegalArgumentException( 211 String.format( 212 "Argument 'value' (%d) was not a valid enum value for type %s " 213 + "(registered? %b)", 214 value, 215 enumType, (registeredValues != null))); 216 } 217 218 return values[ordinal]; 219 } 220 } 221