Home | History | Annotate | Download | only in impl
      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.os.Parcel;
     22 import android.os.Parcelable;
     23 import android.util.Log;
     24 
     25 import java.lang.reflect.Field;
     26 import java.nio.ByteBuffer;
     27 
     28 import static android.hardware.camera2.impl.CameraMetadataNative.*;
     29 import static android.hardware.camera2.marshal.MarshalHelpers.*;
     30 
     31 /**
     32  * Marshal any {@code T extends Parcelable} to/from any native type
     33  *
     34  * <p>Use with extreme caution! File descriptors and binders will not be marshaled across.</p>
     35  */
     36 public class MarshalQueryableParcelable<T extends Parcelable>
     37         implements MarshalQueryable<T> {
     38 
     39     private static final String TAG = "MarshalParcelable";
     40     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     41 
     42     private static final String FIELD_CREATOR = "CREATOR";
     43 
     44     private class MarshalerParcelable extends Marshaler<T> {
     45 
     46         private final Class<T> mClass;
     47         private final Parcelable.Creator<T> mCreator;
     48 
     49         @SuppressWarnings("unchecked")
     50         protected MarshalerParcelable(TypeReference<T> typeReference,
     51                 int nativeType) {
     52             super(MarshalQueryableParcelable.this, typeReference, nativeType);
     53 
     54             mClass = (Class<T>)typeReference.getRawType();
     55             Field creatorField;
     56             try {
     57                 creatorField = mClass.getDeclaredField(FIELD_CREATOR);
     58             } catch (NoSuchFieldException e) {
     59                 // Impossible. All Parcelable implementations must have a 'CREATOR' static field
     60                 throw new AssertionError(e);
     61             }
     62 
     63             try {
     64                 mCreator = (Parcelable.Creator<T>)creatorField.get(null);
     65             } catch (IllegalAccessException e) {
     66                 // Impossible: All 'CREATOR' static fields must be public
     67                 throw new AssertionError(e);
     68             } catch (IllegalArgumentException e) {
     69                 // Impossible: This is a static field, so null must be ok
     70                 throw new AssertionError(e);
     71             }
     72         }
     73 
     74         @Override
     75         public void marshal(T value, ByteBuffer buffer) {
     76             if (VERBOSE) {
     77                 Log.v(TAG, "marshal " + value);
     78             }
     79 
     80             Parcel parcel = Parcel.obtain();
     81             byte[] parcelContents;
     82 
     83             try {
     84                 value.writeToParcel(parcel, /*flags*/0);
     85 
     86                 if (parcel.hasFileDescriptors()) {
     87                     throw new UnsupportedOperationException(
     88                             "Parcelable " + value + " must not have file descriptors");
     89                 }
     90 
     91                 parcelContents = parcel.marshall();
     92             }
     93             finally {
     94                 parcel.recycle();
     95             }
     96 
     97             if (parcelContents.length == 0) {
     98                 throw new AssertionError("No data marshaled for " + value);
     99             }
    100 
    101             buffer.put(parcelContents);
    102         }
    103 
    104         @Override
    105         public T unmarshal(ByteBuffer buffer) {
    106             if (VERBOSE) {
    107                 Log.v(TAG, "unmarshal, buffer remaining " + buffer.remaining());
    108             }
    109 
    110             /*
    111              * Quadratically slow when marshaling an array of parcelables.
    112              *
    113              * Read out the entire byte buffer as an array, then copy it into the parcel.
    114              *
    115              * Once we unparcel the entire object, advance the byte buffer by only how many
    116              * bytes the parcel actually used up.
    117              *
    118              * Future: If we ever do need to use parcelable arrays, we can do this a little smarter
    119              * by reading out a chunk like 4,8,16,24 each time, but not sure how to detect
    120              * parcels being too short in this case.
    121              *
    122              * Future: Alternatively use Parcel#obtain(long) directly into the native
    123              * pointer of a ByteBuffer, which would not copy if the ByteBuffer was direct.
    124              */
    125             buffer.mark();
    126 
    127             Parcel parcel = Parcel.obtain();
    128             try {
    129                 int maxLength = buffer.remaining();
    130 
    131                 byte[] remaining = new byte[maxLength];
    132                 buffer.get(remaining);
    133 
    134                 parcel.unmarshall(remaining, /*offset*/0, maxLength);
    135                 parcel.setDataPosition(/*pos*/0);
    136 
    137                 T value = mCreator.createFromParcel(parcel);
    138                 int actualLength = parcel.dataPosition();
    139 
    140                 if (actualLength == 0) {
    141                     throw new AssertionError("No data marshaled for " + value);
    142                 }
    143 
    144                 // set the position past the bytes the parcelable actually used
    145                 buffer.reset();
    146                 buffer.position(buffer.position() + actualLength);
    147 
    148                 if (VERBOSE) {
    149                     Log.v(TAG, "unmarshal, parcel length was " + actualLength);
    150                     Log.v(TAG, "unmarshal, value is " + value);
    151                 }
    152 
    153                 return mClass.cast(value);
    154             } finally {
    155                 parcel.recycle();
    156             }
    157         }
    158 
    159         @Override
    160         public int getNativeSize() {
    161             return NATIVE_SIZE_DYNAMIC;
    162         }
    163 
    164         @Override
    165         public int calculateMarshalSize(T value) {
    166             Parcel parcel = Parcel.obtain();
    167             try {
    168                 value.writeToParcel(parcel, /*flags*/0);
    169                 int length = parcel.marshall().length;
    170 
    171                 if (VERBOSE) {
    172                     Log.v(TAG, "calculateMarshalSize, length when parceling "
    173                             + value + " is " + length);
    174                 }
    175 
    176                 return length;
    177             } finally {
    178                 parcel.recycle();
    179             }
    180         }
    181     }
    182 
    183     @Override
    184     public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) {
    185         return new MarshalerParcelable(managedType, nativeType);
    186     }
    187 
    188     @Override
    189     public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) {
    190         return Parcelable.class.isAssignableFrom(managedType.getRawType());
    191     }
    192 
    193 }
    194