ItsSerializer.java revision a04e462a07854595122f19b1df9f19c78e5dc030
1/*
2 * Copyright (C) 2013 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
17package com.android.camera2.its;
18
19import android.hardware.camera2.CameraDevice;
20import android.hardware.camera2.CameraMetadata;
21import android.hardware.camera2.CaptureRequest;
22import android.hardware.camera2.Rational;
23import android.util.Log;
24
25import org.json.JSONArray;
26import org.json.JSONObject;
27
28import java.lang.reflect.Array;
29import java.lang.reflect.Field;
30import java.lang.reflect.GenericArrayType;
31import java.lang.reflect.Modifier;
32import java.lang.reflect.ParameterizedType;
33import java.lang.reflect.Type;
34import java.util.Arrays;
35import java.util.LinkedList;
36import java.util.List;
37
38/**
39 * Class to deal with serializing and deserializing between JSON and Camera2 objects.
40 */
41public class ItsSerializer {
42    public static final String TAG = ItsSerializer.class.getSimpleName();
43
44    private static class MetadataEntry {
45        public MetadataEntry(String k, Object v) {
46            key = k;
47            value = v;
48        }
49        public String key;
50        public Object value;
51    }
52
53    @SuppressWarnings("unchecked")
54    private static MetadataEntry serializeEntry(Type keyType, Object keyObj, CameraMetadata md)
55            throws ItsException {
56        CameraMetadata.Key key = (CameraMetadata.Key)keyObj;
57        try {
58            if (md.get(key) == null) {
59                return new MetadataEntry(key.getName(), JSONObject.NULL);
60            }
61            if (keyType == Integer.class || keyType == Long.class    || keyType == Byte.class ||
62                keyType == Float.class   || keyType == Boolean.class || keyType == String.class) {
63                return new MetadataEntry(key.getName(), md.get(key));
64            } else if (keyType == Rational.class) {
65                CameraMetadata.Key<Rational> key2 = (CameraMetadata.Key<Rational>)keyObj;
66                JSONObject ratObj = new JSONObject();
67                ratObj.put("numerator", md.get(key2).getNumerator());
68                ratObj.put("denominator", md.get(key2).getDenominator());
69                return new MetadataEntry(key.getName(), ratObj);
70            } else if (keyType == android.hardware.camera2.Size.class) {
71                CameraMetadata.Key<android.hardware.camera2.Size> key2 =
72                        (CameraMetadata.Key<android.hardware.camera2.Size>)keyObj;
73                JSONObject sizeObj = new JSONObject();
74                sizeObj.put("width", md.get(key2).getWidth());
75                sizeObj.put("height", md.get(key2).getHeight());
76                return new MetadataEntry(key.getName(), sizeObj);
77            } else if (keyType == android.graphics.Rect.class) {
78                CameraMetadata.Key<android.graphics.Rect> key2 =
79                        (CameraMetadata.Key<android.graphics.Rect>)keyObj;
80                JSONObject rectObj = new JSONObject();
81                rectObj.put("left", md.get(key2).left);
82                rectObj.put("right", md.get(key2).right);
83                rectObj.put("top", md.get(key2).top);
84                rectObj.put("bottom", md.get(key2).bottom);
85                return new MetadataEntry(key.getName(), rectObj);
86            } else {
87                throw new ItsException(
88                        "Unsupported key type in metadata serializer: " + keyType);
89            }
90        } catch (org.json.JSONException e) {
91            throw new ItsException("JSON error for key: " + key.getName() + ": ", e);
92        }
93    }
94
95    @SuppressWarnings("unchecked")
96    private static MetadataEntry serializeArrayEntry(Type keyType, Object keyObj, CameraMetadata md)
97            throws ItsException {
98        CameraMetadata.Key key = (CameraMetadata.Key)keyObj;
99        try {
100            if (md.get(key) == null) {
101                return new MetadataEntry(key.getName(), JSONObject.NULL);
102            }
103            Type elmtType = ((GenericArrayType)keyType).getGenericComponentType();
104            if (elmtType == int.class  || elmtType == float.class || elmtType == byte.class ||
105                elmtType == long.class || elmtType == double.class) {
106                return new MetadataEntry(key.getName(), new JSONArray(md.get(key)));
107            } else if (elmtType == Rational.class) {
108                CameraMetadata.Key<Rational[]> key2 = (CameraMetadata.Key<Rational[]>)keyObj;
109                JSONArray jsonArray = new JSONArray();
110                for (int i = 0; i < Array.getLength(md.get(key)); i++) {
111                    JSONObject ratObj = new JSONObject();
112                    ratObj.put("numerator", md.get(key2)[i].getNumerator());
113                    ratObj.put("denominator", md.get(key2)[i].getDenominator());
114                    jsonArray.put(ratObj);
115                }
116                return new MetadataEntry(key.getName(), jsonArray);
117            } else if (elmtType == android.hardware.camera2.Size.class) {
118                CameraMetadata.Key<android.hardware.camera2.Size[]> key2 =
119                        (CameraMetadata.Key<android.hardware.camera2.Size[]>)keyObj;
120                JSONArray jsonArray = new JSONArray();
121                for (int i = 0; i < Array.getLength(md.get(key)); i++) {
122                    JSONObject sizeObj = new JSONObject();
123                    sizeObj.put("width", md.get(key2)[i].getWidth());
124                    sizeObj.put("height", md.get(key2)[i].getHeight());
125                    jsonArray.put(sizeObj);
126                }
127                return new MetadataEntry(key.getName(), jsonArray);
128            } else if (elmtType == android.graphics.Rect.class) {
129                CameraMetadata.Key<android.graphics.Rect[]> key2 =
130                        (CameraMetadata.Key<android.graphics.Rect[]>)keyObj;
131                JSONArray jsonArray = new JSONArray();
132                for (int i = 0; i < Array.getLength(md.get(key)); i++) {
133                    JSONObject rectObj = new JSONObject();
134                    rectObj.put("left", md.get(key2)[i].left);
135                    rectObj.put("right", md.get(key2)[i].right);
136                    rectObj.put("top", md.get(key2)[i].top);
137                    rectObj.put("bottom", md.get(key2)[i].bottom);
138                    jsonArray.put(rectObj);
139                }
140                return new MetadataEntry(key.getName(), jsonArray);
141            } else {
142                throw new ItsException("Unsupported array type: " + elmtType);
143            }
144        } catch (org.json.JSONException e) {
145            throw new ItsException("JSON error for key: " + key.getName() + ": ", e);
146        }
147    }
148
149    @SuppressWarnings("unchecked")
150    public static JSONObject serialize(CameraMetadata md)
151            throws ItsException {
152        JSONObject jsonObj = new JSONObject();
153        Field[] allFields = md.getClass().getDeclaredFields();
154        for (Field field : allFields) {
155            if (Modifier.isPublic(field.getModifiers()) &&
156                    Modifier.isStatic(field.getModifiers()) &&
157                    field.getType() == CameraMetadata.Key.class &&
158                    field.getGenericType() instanceof ParameterizedType) {
159                ParameterizedType paramType = (ParameterizedType)field.getGenericType();
160                Type[] argTypes = paramType.getActualTypeArguments();
161                if (argTypes.length > 0) {
162                    try {
163                        Type keyType = argTypes[0];
164                        Object keyObj = field.get(md);
165                        MetadataEntry entry;
166                        if (keyType instanceof GenericArrayType) {
167                            entry = serializeArrayEntry(keyType, keyObj, md);
168                        } else {
169                            entry = serializeEntry(keyType, keyObj, md);
170                        }
171                        if (entry != null) {
172                            jsonObj.put(entry.key, entry.value);
173                        }
174                    } catch (IllegalAccessException e) {
175                        throw new ItsException(
176                                "Access error for field: " + field + ": ", e);
177                    } catch (org.json.JSONException e) {
178                        throw new ItsException(
179                                "JSON error for field: " + field + ": ", e);
180                    }
181                }
182            }
183        }
184        return jsonObj;
185    }
186
187    @SuppressWarnings("unchecked")
188    public static CaptureRequest.Builder deserialize(CaptureRequest.Builder mdDefault,
189            JSONObject jsonReq) throws ItsException {
190        try {
191            Log.i(TAG, "Parsing JSON capture request ...");
192
193            // Iterate over the CameraMetadata reflected fields.
194            CaptureRequest.Builder md = mdDefault;
195            Field[] allFields = md.getClass().getDeclaredFields();
196            for (Field field : allFields) {
197                if (Modifier.isPublic(field.getModifiers()) &&
198                        Modifier.isStatic(field.getModifiers()) &&
199                        field.getType() == CameraMetadata.Key.class &&
200                        field.getGenericType() instanceof ParameterizedType) {
201                    ParameterizedType paramType = (ParameterizedType)field.getGenericType();
202                    Type[] argTypes = paramType.getActualTypeArguments();
203                    if (argTypes.length > 0) {
204                        CameraMetadata.Key key = (CameraMetadata.Key)field.get(md);
205                        String keyName = key.getName();
206                        Type keyType = argTypes[0];
207
208                        // For each reflected CameraMetadata entry, look inside the JSON object
209                        // to see if it is being set. If it is found, remove the key from the
210                        // JSON object. After this process, there should be no keys left in the
211                        // JSON (otherwise an invalid key was specified).
212
213                        if (jsonReq.has(keyName) && !jsonReq.isNull(keyName)) {
214                            if (keyType instanceof GenericArrayType) {
215                                Type elmtType =
216                                        ((GenericArrayType)keyType).getGenericComponentType();
217                                JSONArray ja = jsonReq.getJSONArray(keyName);
218                                Object val[] = new Object[ja.length()];
219                                for (int i = 0; i < ja.length(); i++) {
220                                    if (elmtType == int.class) {
221                                        Array.set(val, i, ja.getInt(i));
222                                    } else if (elmtType == byte.class) {
223                                        Array.set(val, i, (byte)ja.getInt(i));
224                                    } else if (elmtType == float.class) {
225                                        Array.set(val, i, (float)ja.getDouble(i));
226                                    } else if (elmtType == long.class) {
227                                        Array.set(val, i, ja.getLong(i));
228                                    } else if (elmtType == double.class) {
229                                        Array.set(val, i, ja.getDouble(i));
230                                    } else if (elmtType == boolean.class) {
231                                        Array.set(val, i, ja.getBoolean(i));
232                                    } else if (elmtType == String.class) {
233                                        Array.set(val, i, ja.getString(i));
234                                    } else if (elmtType == android.hardware.camera2.Size.class) {
235                                        JSONObject obj = ja.getJSONObject(i);
236                                        Array.set(val, i, new android.hardware.camera2.Size(
237                                                obj.getInt("width"), obj.getInt("height")));
238                                    } else if (elmtType == android.graphics.Rect.class) {
239                                        JSONObject obj = ja.getJSONObject(i);
240                                        Array.set(val, i, new android.graphics.Rect(
241                                                obj.getInt("left"), obj.getInt("top"),
242                                                obj.getInt("bottom"), obj.getInt("right")));
243                                    } else if (elmtType == Rational.class) {
244                                        JSONObject obj = ja.getJSONObject(i);
245                                        Array.set(val, i, new Rational(
246                                                obj.getInt("numerator"),
247                                                obj.getInt("denominator")));
248                                    } else {
249                                        throw new ItsException(
250                                                "Failed to parse key from JSON: " + keyName);
251                                    }
252                                }
253                                if (val != null) {
254                                    Log.i(TAG, "Set: " + keyName + " -> " + Arrays.toString(val));
255                                    md.set(key, val);
256                                    jsonReq.remove(keyName);
257                                }
258                            } else {
259                                Object val = null;
260                                if (keyType == Integer.class) {
261                                    val = jsonReq.getInt(keyName);
262                                } else if (keyType == Byte.class) {
263                                    val = (byte)jsonReq.getInt(keyName);
264                                } else if (keyType == Double.class) {
265                                    val = jsonReq.getDouble(keyName);
266                                } else if (keyType == Long.class) {
267                                    val = jsonReq.getLong(keyName);
268                                } else if (keyType == Float.class) {
269                                    val = (float)jsonReq.getDouble(keyName);
270                                } else if (keyType == Boolean.class) {
271                                    val = jsonReq.getBoolean(keyName);
272                                } else if (keyType == String.class) {
273                                    val = jsonReq.getString(keyName);
274                                } else if (keyType == android.hardware.camera2.Size.class) {
275                                    JSONObject obj = jsonReq.getJSONObject(keyName);
276                                    val = new android.hardware.camera2.Size(
277                                            obj.getInt("width"), obj.getInt("height"));
278                                } else if (keyType == android.graphics.Rect.class) {
279                                    JSONObject obj = jsonReq.getJSONObject(keyName);
280                                    val = new android.graphics.Rect(
281                                            obj.getInt("left"), obj.getInt("top"),
282                                            obj.getInt("right"), obj.getInt("bottom"));
283                                } else if (keyType == Rational.class) {
284                                    JSONObject obj = jsonReq.getJSONObject(keyName);
285                                    val = new Rational(obj.getInt("numerator"),
286                                                       obj.getInt("denominator"));
287                                } else {
288                                    throw new ItsException(
289                                            "Failed to parse key from JSON: " + keyName);
290                                }
291                                if (val != null) {
292                                    Log.i(TAG, "Set: " + keyName + " -> " + val);
293                                    md.set(key ,val);
294                                    jsonReq.remove(keyName);
295                                }
296                            }
297                        }
298                    }
299                }
300            }
301
302            // Ensure that there were no invalid keys in the JSON request object.
303            if (jsonReq.length() != 0) {
304                throw new ItsException("Invalid JSON key(s): " + jsonReq.toString());
305            }
306
307            Log.i(TAG, "Parsing JSON capture request completed");
308            return md;
309        } catch (java.lang.IllegalAccessException e) {
310            throw new ItsException("Access error: ", e);
311        } catch (org.json.JSONException e) {
312            throw new ItsException("JSON error: ", e);
313        }
314    }
315
316    @SuppressWarnings("unchecked")
317    public static List<CaptureRequest.Builder> deserializeRequestList(
318            CameraDevice device, JSONObject jsonObjTop)
319            throws ItsException {
320        try {
321            List<CaptureRequest.Builder> requests = null;
322            if (jsonObjTop.has("captureRequest")) {
323                JSONObject jsonReq = jsonObjTop.getJSONObject("captureRequest");
324                CaptureRequest.Builder templateReq = device.createCaptureRequest(
325                        CameraDevice.TEMPLATE_STILL_CAPTURE);
326                requests = new LinkedList<CaptureRequest.Builder>();
327                requests.add(deserialize(templateReq, jsonReq));
328            } else if (jsonObjTop.has("captureRequestList")) {
329                JSONArray jsonReqs = jsonObjTop.getJSONArray("captureRequestList");
330                requests = new LinkedList<CaptureRequest.Builder>();
331                for (int i = 0; i < jsonReqs.length(); i++) {
332                    CaptureRequest.Builder templateReq = device.createCaptureRequest(
333                            CameraDevice.TEMPLATE_STILL_CAPTURE);
334                    requests.add(
335                        deserialize(templateReq, jsonReqs.getJSONObject(i)));
336                }
337            }
338            return requests;
339        } catch (org.json.JSONException e) {
340            throw new ItsException("JSON error: ", e);
341        } catch (android.hardware.camera2.CameraAccessException e) {
342            throw new ItsException("Access error: ", e);
343        }
344    }
345}
346