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