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