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 @SuppressWarnings("ReturnValueIgnored") 107 @Override isTypeMappingSupported(TypeReference<T> managedType, int nativeType)108 public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) { 109 if (nativeType == TYPE_INT32 || nativeType == TYPE_BYTE) { 110 if (managedType.getType() instanceof Class<?>) { 111 Class<?> typeClass = (Class<?>)managedType.getType(); 112 113 if (typeClass.isEnum()) { 114 if (DEBUG) { 115 Log.v(TAG, "possible enum detected for " + typeClass); 116 } 117 118 // The enum must not take extra arguments 119 try { 120 // match a class like: "public enum Fruits { Apple, Orange; }" 121 typeClass.getDeclaredConstructor(String.class, int.class); 122 return true; 123 } catch (NoSuchMethodException e) { 124 // Skip: custom enum with a special constructor e.g. Foo(T), but need Foo() 125 Log.e(TAG, "Can't marshal class " + typeClass + "; no default constructor"); 126 } catch (SecurityException e) { 127 // Skip: wouldn't be able to touch the enum anyway 128 Log.e(TAG, "Can't marshal class " + typeClass + "; not accessible"); 129 } 130 } 131 } 132 } 133 134 return false; 135 } 136 137 @SuppressWarnings("rawtypes") 138 private static final HashMap<Class<? extends Enum>, int[]> sEnumValues = 139 new HashMap<Class<? extends Enum>, int[]>(); 140 141 /** 142 * Register a non-sequential set of values to be used with the marshal/unmarshal functions. 143 * 144 * <p>This enables get/set to correctly marshal the enum into a value that is C-compatible.</p> 145 * 146 * @param enumType The class for an enum 147 * @param values A list of values mapping to the ordinals of the enum 148 */ registerEnumValues(Class<T> enumType, int[] values)149 public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) { 150 if (enumType.getEnumConstants().length != values.length) { 151 throw new IllegalArgumentException( 152 "Expected values array to be the same size as the enumTypes values " 153 + values.length + " for type " + enumType); 154 } 155 if (DEBUG) { 156 Log.v(TAG, "Registered enum values for type " + enumType + " values"); 157 } 158 159 sEnumValues.put(enumType, values); 160 } 161 162 /** 163 * Get the numeric value from an enum. 164 * 165 * <p>This is usually the same as the ordinal value for 166 * enums that have fully sequential values, although for C-style enums the range of values 167 * may not map 1:1.</p> 168 * 169 * @param enumValue Enum instance 170 * @return Int guaranteed to be ABI-compatible with the C enum equivalent 171 */ getEnumValue(T enumValue)172 private static <T extends Enum<T>> int getEnumValue(T enumValue) { 173 int[] values; 174 values = sEnumValues.get(enumValue.getClass()); 175 176 int ordinal = enumValue.ordinal(); 177 if (values != null) { 178 return values[ordinal]; 179 } 180 181 return ordinal; 182 } 183 184 /** 185 * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method. 186 * 187 * @param enumType Class of the enum we want to find 188 * @param value The numeric value of the enum 189 * @return An instance of the enum 190 */ getEnumFromValue(Class<T> enumType, int value)191 private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) { 192 int ordinal; 193 194 int[] registeredValues = sEnumValues.get(enumType); 195 if (registeredValues != null) { 196 ordinal = -1; 197 198 for (int i = 0; i < registeredValues.length; ++i) { 199 if (registeredValues[i] == value) { 200 ordinal = i; 201 break; 202 } 203 } 204 } else { 205 ordinal = value; 206 } 207 208 T[] values = enumType.getEnumConstants(); 209 210 if (ordinal < 0 || ordinal >= values.length) { 211 throw new IllegalArgumentException( 212 String.format( 213 "Argument 'value' (%d) was not a valid enum value for type %s " 214 + "(registered? %b)", 215 value, 216 enumType, (registeredValues != null))); 217 } 218 219 return values[ordinal]; 220 } 221 } 222