/* * 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.os.Parcel; import android.os.Parcelable; import android.util.Log; import java.lang.reflect.Field; import java.nio.ByteBuffer; /** * Marshal any {@code T extends Parcelable} to/from any native type * *

Use with extreme caution! File descriptors and binders will not be marshaled across.

*/ public class MarshalQueryableParcelable implements MarshalQueryable { private static final String TAG = "MarshalParcelable"; private static final boolean DEBUG = false; private static final String FIELD_CREATOR = "CREATOR"; private class MarshalerParcelable extends Marshaler { private final Class mClass; private final Parcelable.Creator mCreator; @SuppressWarnings("unchecked") protected MarshalerParcelable(TypeReference typeReference, int nativeType) { super(MarshalQueryableParcelable.this, typeReference, nativeType); mClass = (Class)typeReference.getRawType(); Field creatorField; try { creatorField = mClass.getDeclaredField(FIELD_CREATOR); } catch (NoSuchFieldException e) { // Impossible. All Parcelable implementations must have a 'CREATOR' static field throw new AssertionError(e); } try { mCreator = (Parcelable.Creator)creatorField.get(null); } catch (IllegalAccessException e) { // Impossible: All 'CREATOR' static fields must be public throw new AssertionError(e); } catch (IllegalArgumentException e) { // Impossible: This is a static field, so null must be ok throw new AssertionError(e); } } @Override public void marshal(T value, ByteBuffer buffer) { if (DEBUG) { Log.v(TAG, "marshal " + value); } Parcel parcel = Parcel.obtain(); byte[] parcelContents; try { value.writeToParcel(parcel, /*flags*/0); if (parcel.hasFileDescriptors()) { throw new UnsupportedOperationException( "Parcelable " + value + " must not have file descriptors"); } parcelContents = parcel.marshall(); } finally { parcel.recycle(); } if (parcelContents.length == 0) { throw new AssertionError("No data marshaled for " + value); } buffer.put(parcelContents); } @Override public T unmarshal(ByteBuffer buffer) { if (DEBUG) { Log.v(TAG, "unmarshal, buffer remaining " + buffer.remaining()); } /* * Quadratically slow when marshaling an array of parcelables. * * Read out the entire byte buffer as an array, then copy it into the parcel. * * Once we unparcel the entire object, advance the byte buffer by only how many * bytes the parcel actually used up. * * Future: If we ever do need to use parcelable arrays, we can do this a little smarter * by reading out a chunk like 4,8,16,24 each time, but not sure how to detect * parcels being too short in this case. * * Future: Alternatively use Parcel#obtain(long) directly into the native * pointer of a ByteBuffer, which would not copy if the ByteBuffer was direct. */ buffer.mark(); Parcel parcel = Parcel.obtain(); try { int maxLength = buffer.remaining(); byte[] remaining = new byte[maxLength]; buffer.get(remaining); parcel.unmarshall(remaining, /*offset*/0, maxLength); parcel.setDataPosition(/*pos*/0); T value = mCreator.createFromParcel(parcel); int actualLength = parcel.dataPosition(); if (actualLength == 0) { throw new AssertionError("No data marshaled for " + value); } // set the position past the bytes the parcelable actually used buffer.reset(); buffer.position(buffer.position() + actualLength); if (DEBUG) { Log.v(TAG, "unmarshal, parcel length was " + actualLength); Log.v(TAG, "unmarshal, value is " + value); } return mClass.cast(value); } finally { parcel.recycle(); } } @Override public int getNativeSize() { return NATIVE_SIZE_DYNAMIC; } @Override public int calculateMarshalSize(T value) { Parcel parcel = Parcel.obtain(); try { value.writeToParcel(parcel, /*flags*/0); int length = parcel.marshall().length; if (DEBUG) { Log.v(TAG, "calculateMarshalSize, length when parceling " + value + " is " + length); } return length; } finally { parcel.recycle(); } } } @Override public Marshaler createMarshaler(TypeReference managedType, int nativeType) { return new MarshalerParcelable(managedType, nativeType); } @Override public boolean isTypeMappingSupported(TypeReference managedType, int nativeType) { return Parcelable.class.isAssignableFrom(managedType.getRawType()); } }