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