/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.hardware.camera2.marshal.impl; import android.hardware.camera2.marshal.Marshaler; import android.hardware.camera2.marshal.MarshalQueryable; import android.hardware.camera2.marshal.MarshalRegistry; import android.hardware.camera2.utils.TypeReference; import android.util.Log; import java.lang.reflect.Array; import java.nio.ByteBuffer; import java.util.ArrayList; import static android.hardware.camera2.impl.CameraMetadataNative.*; import static android.hardware.camera2.marshal.MarshalHelpers.*; /** * Marshal any array {@code T}. * *

To marshal any {@code T} to/from a native type, the marshaler for T to/from that native type * also has to exist.

* *

{@code T} can be either a T2[] where T2 is an object type, or a P[] where P is a * built-in primitive (e.g. int[], float[], etc).

* @param the type of the array (e.g. T = int[], or T = Rational[]) */ public class MarshalQueryableArray implements MarshalQueryable { private static final String TAG = MarshalQueryableArray.class.getSimpleName(); private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); private class MarshalerArray extends Marshaler { private final Class mClass; private final Marshaler mComponentMarshaler; private final Class mComponentClass; @SuppressWarnings("unchecked") protected MarshalerArray(TypeReference typeReference, int nativeType) { super(MarshalQueryableArray.this, typeReference, nativeType); mClass = (Class)typeReference.getRawType(); TypeReference componentToken = typeReference.getComponentType(); mComponentMarshaler = MarshalRegistry.getMarshaler(componentToken, mNativeType); mComponentClass = componentToken.getRawType(); } @Override public void marshal(T value, ByteBuffer buffer) { int length = Array.getLength(value); for (int i = 0; i < length; ++i) { marshalArrayElement(mComponentMarshaler, buffer, value, i); } } @Override public T unmarshal(ByteBuffer buffer) { Object array; int elementSize = mComponentMarshaler.getNativeSize(); if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) { int remaining = buffer.remaining(); int arraySize = remaining / elementSize; if (remaining % elementSize != 0) { throw new UnsupportedOperationException("Arrays for " + mTypeReference + " must be packed tighly into a multiple of " + elementSize + "; but there are " + (remaining % elementSize) + " left over bytes"); } if (VERBOSE) { Log.v(TAG, String.format( "Attempting to unpack array (count = %d, element size = %d, bytes " + "remaining = %d) for type %s", arraySize, elementSize, remaining, mClass)); } array = Array.newInstance(mComponentClass, arraySize); for (int i = 0; i < arraySize; ++i) { Object elem = mComponentMarshaler.unmarshal(buffer); Array.set(array, i, elem); } } else { // Dynamic size, use an array list. ArrayList arrayList = new ArrayList(); // Assumes array is packed tightly; no unused bytes allowed while (buffer.hasRemaining()) { Object elem = mComponentMarshaler.unmarshal(buffer); arrayList.add(elem); } int arraySize = arrayList.size(); array = copyListToArray(arrayList, Array.newInstance(mComponentClass, arraySize)); } if (buffer.remaining() != 0) { Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking " + mClass); } return mClass.cast(array); } @Override public int getNativeSize() { return NATIVE_SIZE_DYNAMIC; } @Override public int calculateMarshalSize(T value) { int elementSize = mComponentMarshaler.getNativeSize(); int arrayLength = Array.getLength(value); if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) { // The fast way. Every element size is uniform. return elementSize * arrayLength; } else { // The slow way. Accumulate size for each element. int size = 0; for (int i = 0; i < arrayLength; ++i) { size += calculateElementMarshalSize(mComponentMarshaler, value, i); } return size; } } /* * Helpers to avoid compiler errors regarding types with wildcards (?) */ @SuppressWarnings("unchecked") private void marshalArrayElement(Marshaler marshaler, ByteBuffer buffer, Object array, int index) { marshaler.marshal((TElem)Array.get(array, index), buffer); } @SuppressWarnings("unchecked") private Object copyListToArray(ArrayList arrayList, Object arrayDest) { return arrayList.toArray((T[]) arrayDest); } @SuppressWarnings("unchecked") private int calculateElementMarshalSize(Marshaler marshaler, Object array, int index) { Object elem = Array.get(array, index); return marshaler.calculateMarshalSize((TElem) elem); } } @Override public Marshaler createMarshaler(TypeReference managedType, int nativeType) { return new MarshalerArray(managedType, nativeType); } @Override public boolean isTypeMappingSupported(TypeReference managedType, int nativeType) { // support both ConcreteType[] and GenericType[] return managedType.getRawType().isArray(); // TODO: Should this recurse deeper and check that there is // a valid marshaler for the ConcreteType as well? } }