• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.camera2.its;
18 
19 import android.graphics.Point;
20 import android.graphics.Rect;
21 import android.hardware.camera2.CameraCharacteristics;
22 import android.hardware.camera2.CameraDevice;
23 import android.hardware.camera2.CameraMetadata;
24 import android.hardware.camera2.CaptureResult;
25 import android.hardware.camera2.CaptureRequest;
26 import android.hardware.camera2.TotalCaptureResult;
27 import android.hardware.camera2.params.BlackLevelPattern;
28 import android.hardware.camera2.params.ColorSpaceTransform;
29 import android.hardware.camera2.params.Face;
30 import android.hardware.camera2.params.LensShadingMap;
31 import android.hardware.camera2.params.MeteringRectangle;
32 import android.hardware.camera2.params.RggbChannelVector;
33 import android.hardware.camera2.params.StreamConfigurationMap;
34 import android.hardware.camera2.params.TonemapCurve;
35 import android.location.Location;
36 import android.util.Log;
37 import android.util.Pair;
38 import android.util.Rational;
39 import android.util.Size;
40 import android.util.SizeF;
41 import android.util.Range;
42 
43 import org.json.JSONArray;
44 import org.json.JSONObject;
45 
46 import java.lang.reflect.Array;
47 import java.lang.reflect.Field;
48 import java.lang.reflect.GenericArrayType;
49 import java.lang.reflect.Modifier;
50 import java.lang.reflect.ParameterizedType;
51 import java.lang.reflect.Type;
52 import java.util.Arrays;
53 import java.util.LinkedList;
54 import java.util.List;
55 
56 /**
57  * Class to deal with serializing and deserializing between JSON and Camera2 objects.
58  */
59 public class ItsSerializer {
60     public static final String TAG = ItsSerializer.class.getSimpleName();
61 
62     private static class MetadataEntry {
MetadataEntry(String k, Object v)63         public MetadataEntry(String k, Object v) {
64             key = k;
65             value = v;
66         }
67         public String key;
68         public Object value;
69     }
70 
71     @SuppressWarnings("unchecked")
serializeRational(Rational rat)72     private static Object serializeRational(Rational rat) throws org.json.JSONException {
73         JSONObject ratObj = new JSONObject();
74         ratObj.put("numerator", rat.getNumerator());
75         ratObj.put("denominator", rat.getDenominator());
76         return ratObj;
77     }
78 
79     @SuppressWarnings("unchecked")
serializeSize(Size size)80     private static Object serializeSize(Size size) throws org.json.JSONException {
81         JSONObject sizeObj = new JSONObject();
82         sizeObj.put("width", size.getWidth());
83         sizeObj.put("height", size.getHeight());
84         return sizeObj;
85     }
86 
87     @SuppressWarnings("unchecked")
serializeSizeF(SizeF size)88     private static Object serializeSizeF(SizeF size) throws org.json.JSONException {
89         JSONObject sizeObj = new JSONObject();
90         sizeObj.put("width", size.getWidth());
91         sizeObj.put("height", size.getHeight());
92         return sizeObj;
93     }
94 
95     @SuppressWarnings("unchecked")
serializeRect(Rect rect)96     private static Object serializeRect(Rect rect) throws org.json.JSONException {
97         JSONObject rectObj = new JSONObject();
98         rectObj.put("left", rect.left);
99         rectObj.put("right", rect.right);
100         rectObj.put("top", rect.top);
101         rectObj.put("bottom", rect.bottom);
102         return rectObj;
103     }
104 
serializePoint(Point point)105     private static Object serializePoint(Point point) throws org.json.JSONException {
106         JSONObject pointObj = new JSONObject();
107         pointObj.put("x", point.x);
108         pointObj.put("y", point.y);
109         return pointObj;
110     }
111 
112     @SuppressWarnings("unchecked")
serializeFace(Face face)113     private static Object serializeFace(Face face)
114             throws org.json.JSONException {
115         JSONObject faceObj = new JSONObject();
116         faceObj.put("bounds", serializeRect(face.getBounds()));
117         faceObj.put("score", face.getScore());
118         faceObj.put("id", face.getId());
119         faceObj.put("leftEye", serializePoint(face.getLeftEyePosition()));
120         faceObj.put("rightEye", serializePoint(face.getRightEyePosition()));
121         faceObj.put("mouth", serializePoint(face.getMouthPosition()));
122         return faceObj;
123     }
124 
125     @SuppressWarnings("unchecked")
serializeStreamConfigurationMap( StreamConfigurationMap map)126     private static Object serializeStreamConfigurationMap(
127             StreamConfigurationMap map)
128             throws org.json.JSONException {
129         // TODO: Serialize the rest of the StreamConfigurationMap fields.
130         JSONObject mapObj = new JSONObject();
131         JSONArray cfgArray = new JSONArray();
132         int fmts[] = map.getOutputFormats();
133         if (fmts != null) {
134             for (int fi = 0; fi < Array.getLength(fmts); fi++) {
135                 Size sizes[] = map.getOutputSizes(fmts[fi]);
136                 if (sizes != null) {
137                     for (int si = 0; si < Array.getLength(sizes); si++) {
138                         JSONObject obj = new JSONObject();
139                         obj.put("format", fmts[fi]);
140                         obj.put("width",sizes[si].getWidth());
141                         obj.put("height", sizes[si].getHeight());
142                         obj.put("input", false);
143                         obj.put("minFrameDuration",
144                                 map.getOutputMinFrameDuration(fmts[fi],sizes[si]));
145                         cfgArray.put(obj);
146                     }
147                 }
148             }
149         }
150         mapObj.put("availableStreamConfigurations", cfgArray);
151         return mapObj;
152     }
153 
154     @SuppressWarnings("unchecked")
serializeMeteringRectangle(MeteringRectangle rect)155     private static Object serializeMeteringRectangle(MeteringRectangle rect)
156             throws org.json.JSONException {
157         JSONObject rectObj = new JSONObject();
158         rectObj.put("x", rect.getX());
159         rectObj.put("y", rect.getY());
160         rectObj.put("width", rect.getWidth());
161         rectObj.put("height", rect.getHeight());
162         rectObj.put("weight", rect.getMeteringWeight());
163         return rectObj;
164     }
165 
166     @SuppressWarnings("unchecked")
serializePair(Pair pair)167     private static Object serializePair(Pair pair)
168             throws org.json.JSONException {
169         JSONArray pairObj = new JSONArray();
170         pairObj.put(pair.first);
171         pairObj.put(pair.second);
172         return pairObj;
173     }
174 
175     @SuppressWarnings("unchecked")
serializeRange(Range range)176     private static Object serializeRange(Range range)
177             throws org.json.JSONException {
178         JSONArray rangeObj = new JSONArray();
179         rangeObj.put(range.getLower());
180         rangeObj.put(range.getUpper());
181         return rangeObj;
182     }
183 
184     @SuppressWarnings("unchecked")
serializeColorSpaceTransform(ColorSpaceTransform xform)185     private static Object serializeColorSpaceTransform(ColorSpaceTransform xform)
186             throws org.json.JSONException {
187         JSONArray xformObj = new JSONArray();
188         for (int row = 0; row < 3; row++) {
189             for (int col = 0; col < 3; col++) {
190                 xformObj.put(serializeRational(xform.getElement(col,row)));
191             }
192         }
193         return xformObj;
194     }
195 
196     @SuppressWarnings("unchecked")
serializeTonemapCurve(TonemapCurve curve)197     private static Object serializeTonemapCurve(TonemapCurve curve)
198             throws org.json.JSONException {
199         JSONObject curveObj = new JSONObject();
200         String names[] = {"red", "green", "blue"};
201         for (int ch = 0; ch < 3; ch++) {
202             JSONArray curveArr = new JSONArray();
203             int len = curve.getPointCount(ch);
204             for (int i = 0; i < len; i++) {
205                 curveArr.put(curve.getPoint(ch,i).x);
206                 curveArr.put(curve.getPoint(ch,i).y);
207             }
208             curveObj.put(names[ch], curveArr);
209         }
210         return curveObj;
211     }
212 
213     @SuppressWarnings("unchecked")
serializeRggbChannelVector(RggbChannelVector vec)214     private static Object serializeRggbChannelVector(RggbChannelVector vec)
215             throws org.json.JSONException {
216         JSONArray vecObj = new JSONArray();
217         vecObj.put(vec.getRed());
218         vecObj.put(vec.getGreenEven());
219         vecObj.put(vec.getGreenOdd());
220         vecObj.put(vec.getBlue());
221         return vecObj;
222     }
223 
224     @SuppressWarnings("unchecked")
serializeBlackLevelPattern(BlackLevelPattern pat)225     private static Object serializeBlackLevelPattern(BlackLevelPattern pat)
226             throws org.json.JSONException {
227         int patVals[] = new int[4];
228         pat.copyTo(patVals, 0);
229         JSONArray patObj = new JSONArray();
230         patObj.put(patVals[0]);
231         patObj.put(patVals[1]);
232         patObj.put(patVals[2]);
233         patObj.put(patVals[3]);
234         return patObj;
235     }
236 
237     @SuppressWarnings("unchecked")
serializeLocation(Location loc)238     private static Object serializeLocation(Location loc)
239             throws org.json.JSONException {
240         return loc.toString();
241     }
242 
243     @SuppressWarnings("unchecked")
serializeLensShadingMap(LensShadingMap map)244     private static Object serializeLensShadingMap(LensShadingMap map)
245             throws org.json.JSONException {
246         JSONArray mapObj = new JSONArray();
247         for (int row = 0; row < map.getRowCount(); row++) {
248             for (int col = 0; col < map.getColumnCount(); col++) {
249                 for (int ch = 0; ch < 4; ch++) {
250                     mapObj.put(map.getGainFactor(ch, col, row));
251                 }
252             }
253         }
254         return mapObj;
255     }
256 
getKeyName(Object keyObj)257     private static String getKeyName(Object keyObj) throws ItsException {
258         if (keyObj.getClass() == CaptureResult.Key.class
259                 || keyObj.getClass() == TotalCaptureResult.class) {
260             return ((CaptureResult.Key)keyObj).getName();
261         } else if (keyObj.getClass() == CaptureRequest.Key.class) {
262             return ((CaptureRequest.Key)keyObj).getName();
263         } else if (keyObj.getClass() == CameraCharacteristics.Key.class) {
264             return ((CameraCharacteristics.Key)keyObj).getName();
265         }
266         throw new ItsException("Invalid key object");
267     }
268 
getKeyValue(CameraMetadata md, Object keyObj)269     private static Object getKeyValue(CameraMetadata md, Object keyObj) throws ItsException {
270         if (md.getClass() == CaptureResult.class || md.getClass() == TotalCaptureResult.class) {
271             return ((CaptureResult)md).get((CaptureResult.Key)keyObj);
272         } else if (md.getClass() == CaptureRequest.class) {
273             return ((CaptureRequest)md).get((CaptureRequest.Key)keyObj);
274         } else if (md.getClass() == CameraCharacteristics.class) {
275             return ((CameraCharacteristics)md).get((CameraCharacteristics.Key)keyObj);
276         }
277         throw new ItsException("Invalid key object");
278     }
279 
280     @SuppressWarnings("unchecked")
serializeEntry(Type keyType, Object keyObj, CameraMetadata md)281     private static MetadataEntry serializeEntry(Type keyType, Object keyObj, CameraMetadata md)
282             throws ItsException {
283         String keyName = getKeyName(keyObj);
284 
285         try {
286             Object keyValue = getKeyValue(md, keyObj);
287             if (keyValue == null) {
288                 return new MetadataEntry(keyName, JSONObject.NULL);
289             } else if (keyType == Float.class) {
290                 // The JSON serializer doesn't handle floating point NaN or Inf.
291                 if (((Float)keyValue).isInfinite() || ((Float)keyValue).isNaN()) {
292                     Logt.w(TAG, "Inf/NaN floating point value serialized: " + keyName);
293                     return null;
294                 }
295                 return new MetadataEntry(keyName, keyValue);
296             } else if (keyType == Integer.class || keyType == Long.class || keyType == Byte.class ||
297                        keyType == Boolean.class || keyType == String.class) {
298                 return new MetadataEntry(keyName, keyValue);
299             } else if (keyType == Rational.class) {
300                 return new MetadataEntry(keyName, serializeRational((Rational)keyValue));
301             } else if (keyType == Size.class) {
302                 return new MetadataEntry(keyName, serializeSize((Size)keyValue));
303             } else if (keyType == SizeF.class) {
304                 return new MetadataEntry(keyName, serializeSizeF((SizeF)keyValue));
305             } else if (keyType == Rect.class) {
306                 return new MetadataEntry(keyName, serializeRect((Rect)keyValue));
307             } else if (keyType == Face.class) {
308                 return new MetadataEntry(keyName, serializeFace((Face)keyValue));
309             } else if (keyType == StreamConfigurationMap.class) {
310                 return new MetadataEntry(keyName,
311                         serializeStreamConfigurationMap((StreamConfigurationMap)keyValue));
312             } else if (keyType instanceof ParameterizedType &&
313                     ((ParameterizedType)keyType).getRawType() == Range.class) {
314                 return new MetadataEntry(keyName, serializeRange((Range)keyValue));
315             } else if (keyType == ColorSpaceTransform.class) {
316                 return new MetadataEntry(keyName,
317                         serializeColorSpaceTransform((ColorSpaceTransform)keyValue));
318             } else if (keyType == MeteringRectangle.class) {
319                 return new MetadataEntry(keyName,
320                         serializeMeteringRectangle((MeteringRectangle)keyValue));
321             } else if (keyType == Location.class) {
322                 return new MetadataEntry(keyName,
323                         serializeLocation((Location)keyValue));
324             } else if (keyType == RggbChannelVector.class) {
325                 return new MetadataEntry(keyName,
326                         serializeRggbChannelVector((RggbChannelVector)keyValue));
327             } else if (keyType == BlackLevelPattern.class) {
328                 return new MetadataEntry(keyName,
329                         serializeBlackLevelPattern((BlackLevelPattern)keyValue));
330             } else if (keyType == TonemapCurve.class) {
331                 return new MetadataEntry(keyName,
332                         serializeTonemapCurve((TonemapCurve)keyValue));
333             } else if (keyType == Point.class) {
334                 return new MetadataEntry(keyName,
335                         serializePoint((Point)keyValue));
336             } else if (keyType == LensShadingMap.class) {
337                 return new MetadataEntry(keyName,
338                         serializeLensShadingMap((LensShadingMap)keyValue));
339             } else {
340                 Logt.w(TAG, String.format("Serializing unsupported key type: " + keyType));
341                 return null;
342             }
343         } catch (org.json.JSONException e) {
344             throw new ItsException("JSON error for key: " + keyName + ": ", e);
345         }
346     }
347 
348     @SuppressWarnings("unchecked")
serializeArrayEntry(Type keyType, Object keyObj, CameraMetadata md)349     private static MetadataEntry serializeArrayEntry(Type keyType, Object keyObj, CameraMetadata md)
350             throws ItsException {
351         String keyName = getKeyName(keyObj);
352         try {
353             Object keyValue = getKeyValue(md, keyObj);
354             if (keyValue == null) {
355                 return new MetadataEntry(keyName, JSONObject.NULL);
356             }
357             int arrayLen = Array.getLength(keyValue);
358             Type elmtType = ((GenericArrayType)keyType).getGenericComponentType();
359             if (elmtType == int.class  || elmtType == float.class || elmtType == byte.class ||
360                 elmtType == long.class || elmtType == double.class || elmtType == boolean.class) {
361                 return new MetadataEntry(keyName, new JSONArray(keyValue));
362             } else if (elmtType == Rational.class) {
363                 JSONArray jsonArray = new JSONArray();
364                 for (int i = 0; i < arrayLen; i++) {
365                     jsonArray.put(serializeRational((Rational)Array.get(keyValue,i)));
366                 }
367                 return new MetadataEntry(keyName, jsonArray);
368             } else if (elmtType == Size.class) {
369                 JSONArray jsonArray = new JSONArray();
370                 for (int i = 0; i < arrayLen; i++) {
371                     jsonArray.put(serializeSize((Size)Array.get(keyValue,i)));
372                 }
373                 return new MetadataEntry(keyName, jsonArray);
374             } else if (elmtType == Rect.class) {
375                 JSONArray jsonArray = new JSONArray();
376                 for (int i = 0; i < arrayLen; i++) {
377                     jsonArray.put(serializeRect((Rect)Array.get(keyValue,i)));
378                 }
379                 return new MetadataEntry(keyName, jsonArray);
380             } else if (elmtType == Face.class) {
381                 JSONArray jsonArray = new JSONArray();
382                 for (int i = 0; i < arrayLen; i++) {
383                     jsonArray.put(serializeFace((Face)Array.get(keyValue, i)));
384                 }
385                 return new MetadataEntry(keyName, jsonArray);
386             } else if (elmtType == StreamConfigurationMap.class) {
387                 JSONArray jsonArray = new JSONArray();
388                 for (int i = 0; i < arrayLen; i++) {
389                     jsonArray.put(serializeStreamConfigurationMap(
390                             (StreamConfigurationMap)Array.get(keyValue,i)));
391                 }
392                 return new MetadataEntry(keyName, jsonArray);
393             } else if (elmtType instanceof ParameterizedType &&
394                     ((ParameterizedType)elmtType).getRawType() == Range.class) {
395                 JSONArray jsonArray = new JSONArray();
396                 for (int i = 0; i < arrayLen; i++) {
397                     jsonArray.put(serializeRange((Range)Array.get(keyValue,i)));
398                 }
399                 return new MetadataEntry(keyName, jsonArray);
400             } else if (elmtType instanceof ParameterizedType &&
401                     ((ParameterizedType)elmtType).getRawType() == Pair.class) {
402                 JSONArray jsonArray = new JSONArray();
403                 for (int i = 0; i < arrayLen; i++) {
404                     jsonArray.put(serializePair((Pair)Array.get(keyValue,i)));
405                 }
406                 return new MetadataEntry(keyName, jsonArray);
407             } else if (elmtType == MeteringRectangle.class) {
408                 JSONArray jsonArray = new JSONArray();
409                 for (int i = 0; i < arrayLen; i++) {
410                     jsonArray.put(serializeMeteringRectangle(
411                             (MeteringRectangle)Array.get(keyValue,i)));
412                 }
413                 return new MetadataEntry(keyName, jsonArray);
414             } else if (elmtType == Location.class) {
415                 JSONArray jsonArray = new JSONArray();
416                 for (int i = 0; i < arrayLen; i++) {
417                     jsonArray.put(serializeLocation((Location)Array.get(keyValue,i)));
418                 }
419                 return new MetadataEntry(keyName, jsonArray);
420             } else if (elmtType == RggbChannelVector.class) {
421                 JSONArray jsonArray = new JSONArray();
422                 for (int i = 0; i < arrayLen; i++) {
423                     jsonArray.put(serializeRggbChannelVector(
424                             (RggbChannelVector)Array.get(keyValue,i)));
425                 }
426                 return new MetadataEntry(keyName, jsonArray);
427             } else if (elmtType == BlackLevelPattern.class) {
428                 JSONArray jsonArray = new JSONArray();
429                 for (int i = 0; i < arrayLen; i++) {
430                     jsonArray.put(serializeBlackLevelPattern(
431                             (BlackLevelPattern)Array.get(keyValue,i)));
432                 }
433                 return new MetadataEntry(keyName, jsonArray);
434             } else if (elmtType == Point.class) {
435                 JSONArray jsonArray = new JSONArray();
436                 for (int i = 0; i < arrayLen; i++) {
437                     jsonArray.put(serializePoint((Point)Array.get(keyValue,i)));
438                 }
439                 return new MetadataEntry(keyName, jsonArray);
440             } else {
441                 Logt.w(TAG, String.format("Serializing unsupported array type: " + elmtType));
442                 return null;
443             }
444         } catch (org.json.JSONException e) {
445             throw new ItsException("JSON error for key: " + keyName + ": ", e);
446         }
447     }
448 
449     @SuppressWarnings("unchecked")
serialize(CameraMetadata md)450     public static JSONObject serialize(CameraMetadata md)
451             throws ItsException {
452         JSONObject jsonObj = new JSONObject();
453         Field[] allFields = md.getClass().getDeclaredFields();
454         if (md.getClass() == TotalCaptureResult.class) {
455             allFields = CaptureResult.class.getDeclaredFields();
456         }
457         for (Field field : allFields) {
458             if (Modifier.isPublic(field.getModifiers()) &&
459                     Modifier.isStatic(field.getModifiers()) &&
460                     (field.getType() == CaptureRequest.Key.class
461                       || field.getType() == CaptureResult.Key.class
462                       || field.getType() == TotalCaptureResult.Key.class
463                       || field.getType() == CameraCharacteristics.Key.class) &&
464                     field.getGenericType() instanceof ParameterizedType) {
465                 ParameterizedType paramType = (ParameterizedType)field.getGenericType();
466                 Type[] argTypes = paramType.getActualTypeArguments();
467                 if (argTypes.length > 0) {
468                     try {
469                         Type keyType = argTypes[0];
470                         Object keyObj = field.get(md);
471                         MetadataEntry entry;
472                         if (keyType instanceof GenericArrayType) {
473                             entry = serializeArrayEntry(keyType, keyObj, md);
474                         } else {
475                             entry = serializeEntry(keyType, keyObj, md);
476                         }
477 
478                         // TODO: Figure this weird case out.
479                         // There is a weird case where the entry is non-null but the toString
480                         // of the entry is null, and if this happens, the null-ness spreads like
481                         // a virus and makes the whole JSON object null from the top level down.
482                         // Not sure if it's a bug in the library or I'm just not using it right.
483                         // Workaround by checking for this case explicitly and not adding the
484                         // value to the jsonObj when it is detected.
485                         if (entry != null && entry.key != null && entry.value != null
486                                           && entry.value.toString() == null) {
487                             Logt.w(TAG, "Error encountered serializing value for key: " + entry.key);
488                         } else if (entry != null) {
489                             jsonObj.put(entry.key, entry.value);
490                         } else {
491                             // Ignore.
492                         }
493                     } catch (IllegalAccessException e) {
494                         throw new ItsException(
495                                 "Access error for field: " + field + ": ", e);
496                     } catch (org.json.JSONException e) {
497                         throw new ItsException(
498                                 "JSON error for field: " + field + ": ", e);
499                     }
500                 }
501             }
502         }
503         return jsonObj;
504     }
505 
506     @SuppressWarnings("unchecked")
deserialize(CaptureRequest.Builder mdDefault, JSONObject jsonReq)507     public static CaptureRequest.Builder deserialize(CaptureRequest.Builder mdDefault,
508             JSONObject jsonReq) throws ItsException {
509         try {
510             Logt.i(TAG, "Parsing JSON capture request ...");
511 
512             // Iterate over the CaptureRequest reflected fields.
513             CaptureRequest.Builder md = mdDefault;
514             Field[] allFields = CaptureRequest.class.getDeclaredFields();
515             for (Field field : allFields) {
516                 if (Modifier.isPublic(field.getModifiers()) &&
517                         Modifier.isStatic(field.getModifiers()) &&
518                         field.getType() == CaptureRequest.Key.class &&
519                         field.getGenericType() instanceof ParameterizedType) {
520                     ParameterizedType paramType = (ParameterizedType)field.getGenericType();
521                     Type[] argTypes = paramType.getActualTypeArguments();
522                     if (argTypes.length > 0) {
523                         CaptureRequest.Key key = (CaptureRequest.Key)field.get(md);
524                         String keyName = key.getName();
525                         Type keyType = argTypes[0];
526 
527                         // For each reflected CaptureRequest entry, look inside the JSON object
528                         // to see if it is being set. If it is found, remove the key from the
529                         // JSON object. After this process, there should be no keys left in the
530                         // JSON (otherwise an invalid key was specified).
531 
532                         if (jsonReq.has(keyName) && !jsonReq.isNull(keyName)) {
533                             if (keyType instanceof GenericArrayType) {
534                                 Type elmtType =
535                                         ((GenericArrayType)keyType).getGenericComponentType();
536                                 JSONArray ja = jsonReq.getJSONArray(keyName);
537                                 Object val[] = new Object[ja.length()];
538                                 for (int i = 0; i < ja.length(); i++) {
539                                     if (elmtType == int.class) {
540                                         Array.set(val, i, ja.getInt(i));
541                                     } else if (elmtType == byte.class) {
542                                         Array.set(val, i, (byte)ja.getInt(i));
543                                     } else if (elmtType == float.class) {
544                                         Array.set(val, i, (float)ja.getDouble(i));
545                                     } else if (elmtType == long.class) {
546                                         Array.set(val, i, ja.getLong(i));
547                                     } else if (elmtType == double.class) {
548                                         Array.set(val, i, ja.getDouble(i));
549                                     } else if (elmtType == boolean.class) {
550                                         Array.set(val, i, ja.getBoolean(i));
551                                     } else if (elmtType == String.class) {
552                                         Array.set(val, i, ja.getString(i));
553                                     } else if (elmtType == Size.class){
554                                         JSONObject obj = ja.getJSONObject(i);
555                                         Array.set(val, i, new Size(
556                                                 obj.getInt("width"), obj.getInt("height")));
557                                     } else if (elmtType == Rect.class) {
558                                         JSONObject obj = ja.getJSONObject(i);
559                                         Array.set(val, i, new Rect(
560                                                 obj.getInt("left"), obj.getInt("top"),
561                                                 obj.getInt("bottom"), obj.getInt("right")));
562                                     } else if (elmtType == Rational.class) {
563                                         JSONObject obj = ja.getJSONObject(i);
564                                         Array.set(val, i, new Rational(
565                                                 obj.getInt("numerator"),
566                                                 obj.getInt("denominator")));
567                                     } else if (elmtType == RggbChannelVector.class) {
568                                         JSONArray arr = ja.getJSONArray(i);
569                                         Array.set(val, i, new RggbChannelVector(
570                                                 (float)arr.getDouble(0),
571                                                 (float)arr.getDouble(1),
572                                                 (float)arr.getDouble(2),
573                                                 (float)arr.getDouble(3)));
574                                     } else if (elmtType == ColorSpaceTransform.class) {
575                                         JSONArray arr = ja.getJSONArray(i);
576                                         Rational xform[] = new Rational[9];
577                                         for (int j = 0; j < 9; j++) {
578                                             xform[j] = new Rational(
579                                                     arr.getJSONObject(j).getInt("numerator"),
580                                                     arr.getJSONObject(j).getInt("denominator"));
581                                         }
582                                         Array.set(val, i, new ColorSpaceTransform(xform));
583                                     } else if (elmtType == MeteringRectangle.class) {
584                                         JSONObject obj = ja.getJSONObject(i);
585                                         Array.set(val, i, new MeteringRectangle(
586                                                 obj.getInt("x"),
587                                                 obj.getInt("y"),
588                                                 obj.getInt("width"),
589                                                 obj.getInt("height"),
590                                                 obj.getInt("weight")));
591                                     } else {
592                                         throw new ItsException(
593                                                 "Failed to parse key from JSON: " + keyName);
594                                     }
595                                 }
596                                 if (val != null) {
597                                     Logt.i(TAG, "Set: "+keyName+" -> "+Arrays.toString(val));
598                                     md.set(key, val);
599                                     jsonReq.remove(keyName);
600                                 }
601                             } else {
602                                 Object val = null;
603                                 if (keyType == Integer.class) {
604                                     val = jsonReq.getInt(keyName);
605                                 } else if (keyType == Byte.class) {
606                                     val = (byte)jsonReq.getInt(keyName);
607                                 } else if (keyType == Double.class) {
608                                     val = jsonReq.getDouble(keyName);
609                                 } else if (keyType == Long.class) {
610                                     val = jsonReq.getLong(keyName);
611                                 } else if (keyType == Float.class) {
612                                     val = (float)jsonReq.getDouble(keyName);
613                                 } else if (keyType == Boolean.class) {
614                                     val = jsonReq.getBoolean(keyName);
615                                 } else if (keyType == String.class) {
616                                     val = jsonReq.getString(keyName);
617                                 } else if (keyType == Size.class) {
618                                     JSONObject obj = jsonReq.getJSONObject(keyName);
619                                     val = new Size(
620                                             obj.getInt("width"), obj.getInt("height"));
621                                 } else if (keyType == Rect.class) {
622                                     JSONObject obj = jsonReq.getJSONObject(keyName);
623                                     val = new Rect(
624                                             obj.getInt("left"), obj.getInt("top"),
625                                             obj.getInt("right"), obj.getInt("bottom"));
626                                 } else if (keyType == Rational.class) {
627                                     JSONObject obj = jsonReq.getJSONObject(keyName);
628                                     val = new Rational(obj.getInt("numerator"),
629                                                        obj.getInt("denominator"));
630                                 } else if (keyType == RggbChannelVector.class) {
631                                     JSONObject obj = jsonReq.optJSONObject(keyName);
632                                     JSONArray arr = jsonReq.optJSONArray(keyName);
633                                     if (arr != null) {
634                                         val = new RggbChannelVector(
635                                                 (float)arr.getDouble(0),
636                                                 (float)arr.getDouble(1),
637                                                 (float)arr.getDouble(2),
638                                                 (float)arr.getDouble(3));
639                                     } else if (obj != null) {
640                                         val = new RggbChannelVector(
641                                                 (float)obj.getDouble("red"),
642                                                 (float)obj.getDouble("greenEven"),
643                                                 (float)obj.getDouble("greenOdd"),
644                                                 (float)obj.getDouble("blue"));
645                                     } else {
646                                         throw new ItsException("Invalid RggbChannelVector object");
647                                     }
648                                 } else if (keyType == ColorSpaceTransform.class) {
649                                     JSONArray arr = jsonReq.getJSONArray(keyName);
650                                     Rational a[] = new Rational[9];
651                                     for (int i = 0; i < 9; i++) {
652                                         a[i] = new Rational(
653                                                 arr.getJSONObject(i).getInt("numerator"),
654                                                 arr.getJSONObject(i).getInt("denominator"));
655                                     }
656                                     val = new ColorSpaceTransform(a);
657                                 } else if (keyType instanceof ParameterizedType &&
658                                         ((ParameterizedType)keyType).getRawType() == Range.class &&
659                                         ((ParameterizedType)keyType).getActualTypeArguments().length == 1 &&
660                                         ((ParameterizedType)keyType).getActualTypeArguments()[0] == Integer.class) {
661                                     JSONArray arr = jsonReq.getJSONArray(keyName);
662                                     val = new Range<Integer>(arr.getInt(0), arr.getInt(1));
663                                 } else {
664                                     throw new ItsException(
665                                             "Failed to parse key from JSON: " +
666                                             keyName + ", " + keyType);
667                                 }
668                                 if (val != null) {
669                                     Logt.i(TAG, "Set: " + keyName + " -> " + val);
670                                     md.set(key ,val);
671                                     jsonReq.remove(keyName);
672                                 }
673                             }
674                         }
675                     }
676                 }
677             }
678 
679             // Ensure that there were no invalid keys in the JSON request object.
680             if (jsonReq.length() != 0) {
681                 throw new ItsException("Invalid JSON key(s): " + jsonReq.toString());
682             }
683 
684             Logt.i(TAG, "Parsing JSON capture request completed");
685             return md;
686         } catch (java.lang.IllegalAccessException e) {
687             throw new ItsException("Access error: ", e);
688         } catch (org.json.JSONException e) {
689             throw new ItsException("JSON error: ", e);
690         }
691     }
692 
693     @SuppressWarnings("unchecked")
deserializeRequestList( CameraDevice device, JSONObject jsonObjTop)694     public static List<CaptureRequest.Builder> deserializeRequestList(
695             CameraDevice device, JSONObject jsonObjTop)
696             throws ItsException {
697         try {
698             List<CaptureRequest.Builder> requests = null;
699             JSONArray jsonReqs = jsonObjTop.getJSONArray("captureRequests");
700             requests = new LinkedList<CaptureRequest.Builder>();
701             for (int i = 0; i < jsonReqs.length(); i++) {
702                 CaptureRequest.Builder templateReq = device.createCaptureRequest(
703                         CameraDevice.TEMPLATE_STILL_CAPTURE);
704                 requests.add(
705                     deserialize(templateReq, jsonReqs.getJSONObject(i)));
706             }
707             return requests;
708         } catch (org.json.JSONException e) {
709             throw new ItsException("JSON error: ", e);
710         } catch (android.hardware.camera2.CameraAccessException e) {
711             throw new ItsException("Access error: ", e);
712         }
713     }
714 }
715