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