Home | History | Annotate | Download | only in its
      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 {
     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")
     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")
     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")
     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")
     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 
    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")
    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")
    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")
    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")
    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")
    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")
    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")
    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")
    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")
    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")
    257     private static Object serializeLocation(Location loc)
    258             throws org.json.JSONException {
    259         return loc.toString();
    260     }
    261 
    262     @SuppressWarnings("unchecked")
    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 
    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 
    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")
    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")
    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 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")
    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")
    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")
    713     public static List<CaptureRequest.Builder> deserializeRequestList(
    714             CameraDevice device, JSONObject jsonObjTop, String requestKey)
    715             throws ItsException {
    716         try {
    717             List<CaptureRequest.Builder> requests = null;
    718             JSONArray jsonReqs = jsonObjTop.getJSONArray(requestKey);
    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