MarshalQueryableParcelable.java revision 3c40a046cf0ea7b6af01ec93e5276eccb3234bfe
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 */
16package android.hardware.camera2.marshal.impl;
17
18import android.hardware.camera2.marshal.Marshaler;
19import android.hardware.camera2.marshal.MarshalQueryable;
20import android.hardware.camera2.utils.TypeReference;
21import android.os.Parcel;
22import android.os.Parcelable;
23import android.util.Log;
24
25import java.lang.reflect.Field;
26import java.nio.ByteBuffer;
27
28import static android.hardware.camera2.impl.CameraMetadataNative.*;
29import 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 */
36public 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