/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.camera2.its; import android.graphics.Point; import android.graphics.Rect; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.BlackLevelPattern; import android.hardware.camera2.params.ColorSpaceTransform; import android.hardware.camera2.params.Face; import android.hardware.camera2.params.LensShadingMap; import android.hardware.camera2.params.MeteringRectangle; import android.hardware.camera2.params.RggbChannelVector; import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.params.TonemapCurve; import android.location.Location; import android.util.Log; import android.util.Pair; import android.util.Rational; import android.util.Size; import android.util.SizeF; import android.util.Range; import org.json.JSONArray; import org.json.JSONObject; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.LinkedList; import java.util.List; /** * Class to deal with serializing and deserializing between JSON and Camera2 objects. */ public class ItsSerializer { public static final String TAG = ItsSerializer.class.getSimpleName(); private static class MetadataEntry { public MetadataEntry(String k, Object v) { key = k; value = v; } public String key; public Object value; } @SuppressWarnings("unchecked") private static Object serializeRational(Rational rat) throws org.json.JSONException { JSONObject ratObj = new JSONObject(); ratObj.put("numerator", rat.getNumerator()); ratObj.put("denominator", rat.getDenominator()); return ratObj; } @SuppressWarnings("unchecked") private static Object serializeSize(Size size) throws org.json.JSONException { JSONObject sizeObj = new JSONObject(); sizeObj.put("width", size.getWidth()); sizeObj.put("height", size.getHeight()); return sizeObj; } @SuppressWarnings("unchecked") private static Object serializeSizeF(SizeF size) throws org.json.JSONException { JSONObject sizeObj = new JSONObject(); sizeObj.put("width", size.getWidth()); sizeObj.put("height", size.getHeight()); return sizeObj; } @SuppressWarnings("unchecked") private static Object serializeRect(Rect rect) throws org.json.JSONException { JSONObject rectObj = new JSONObject(); rectObj.put("left", rect.left); rectObj.put("right", rect.right); rectObj.put("top", rect.top); rectObj.put("bottom", rect.bottom); return rectObj; } private static Object serializePoint(Point point) throws org.json.JSONException { JSONObject pointObj = new JSONObject(); pointObj.put("x", point.x); pointObj.put("y", point.y); return pointObj; } @SuppressWarnings("unchecked") private static Object serializeFace(Face face) throws org.json.JSONException { JSONObject faceObj = new JSONObject(); faceObj.put("bounds", serializeRect(face.getBounds())); faceObj.put("score", face.getScore()); faceObj.put("id", face.getId()); faceObj.put("leftEye", serializePoint(face.getLeftEyePosition())); faceObj.put("rightEye", serializePoint(face.getRightEyePosition())); faceObj.put("mouth", serializePoint(face.getMouthPosition())); return faceObj; } @SuppressWarnings("unchecked") private static Object serializeStreamConfigurationMap( StreamConfigurationMap map) throws org.json.JSONException { // TODO: Serialize the rest of the StreamConfigurationMap fields. JSONObject mapObj = new JSONObject(); JSONArray cfgArray = new JSONArray(); int fmts[] = map.getOutputFormats(); if (fmts != null) { for (int fi = 0; fi < Array.getLength(fmts); fi++) { Size sizes[] = map.getOutputSizes(fmts[fi]); if (sizes != null) { for (int si = 0; si < Array.getLength(sizes); si++) { JSONObject obj = new JSONObject(); obj.put("format", fmts[fi]); obj.put("width",sizes[si].getWidth()); obj.put("height", sizes[si].getHeight()); obj.put("input", false); obj.put("minFrameDuration", map.getOutputMinFrameDuration(fmts[fi],sizes[si])); cfgArray.put(obj); } } } } mapObj.put("availableStreamConfigurations", cfgArray); return mapObj; } @SuppressWarnings("unchecked") private static Object serializeMeteringRectangle(MeteringRectangle rect) throws org.json.JSONException { JSONObject rectObj = new JSONObject(); rectObj.put("x", rect.getX()); rectObj.put("y", rect.getY()); rectObj.put("width", rect.getWidth()); rectObj.put("height", rect.getHeight()); rectObj.put("weight", rect.getMeteringWeight()); return rectObj; } @SuppressWarnings("unchecked") private static Object serializePair(Pair pair) throws org.json.JSONException { JSONArray pairObj = new JSONArray(); pairObj.put(pair.first); pairObj.put(pair.second); return pairObj; } @SuppressWarnings("unchecked") private static Object serializeRange(Range range) throws org.json.JSONException { JSONArray rangeObj = new JSONArray(); rangeObj.put(range.getLower()); rangeObj.put(range.getUpper()); return rangeObj; } @SuppressWarnings("unchecked") private static Object serializeColorSpaceTransform(ColorSpaceTransform xform) throws org.json.JSONException { JSONArray xformObj = new JSONArray(); for (int row = 0; row < 3; row++) { for (int col = 0; col < 3; col++) { xformObj.put(serializeRational(xform.getElement(col,row))); } } return xformObj; } @SuppressWarnings("unchecked") private static Object serializeTonemapCurve(TonemapCurve curve) throws org.json.JSONException { JSONObject curveObj = new JSONObject(); String names[] = {"red", "green", "blue"}; for (int ch = 0; ch < 3; ch++) { JSONArray curveArr = new JSONArray(); int len = curve.getPointCount(ch); for (int i = 0; i < len; i++) { curveArr.put(curve.getPoint(ch,i).x); curveArr.put(curve.getPoint(ch,i).y); } curveObj.put(names[ch], curveArr); } return curveObj; } @SuppressWarnings("unchecked") private static Object serializeRggbChannelVector(RggbChannelVector vec) throws org.json.JSONException { JSONArray vecObj = new JSONArray(); vecObj.put(vec.getRed()); vecObj.put(vec.getGreenEven()); vecObj.put(vec.getGreenOdd()); vecObj.put(vec.getBlue()); return vecObj; } @SuppressWarnings("unchecked") private static Object serializeBlackLevelPattern(BlackLevelPattern pat) throws org.json.JSONException { int patVals[] = new int[4]; pat.copyTo(patVals, 0); JSONArray patObj = new JSONArray(); patObj.put(patVals[0]); patObj.put(patVals[1]); patObj.put(patVals[2]); patObj.put(patVals[3]); return patObj; } @SuppressWarnings("unchecked") private static Object serializeLocation(Location loc) throws org.json.JSONException { return loc.toString(); } @SuppressWarnings("unchecked") private static Object serializeLensShadingMap(LensShadingMap map) throws org.json.JSONException { JSONArray mapObj = new JSONArray(); for (int row = 0; row < map.getRowCount(); row++) { for (int col = 0; col < map.getColumnCount(); col++) { for (int ch = 0; ch < 4; ch++) { mapObj.put(map.getGainFactor(ch, col, row)); } } } return mapObj; } private static String getKeyName(Object keyObj) throws ItsException { if (keyObj.getClass() == CaptureResult.Key.class || keyObj.getClass() == TotalCaptureResult.class) { return ((CaptureResult.Key)keyObj).getName(); } else if (keyObj.getClass() == CaptureRequest.Key.class) { return ((CaptureRequest.Key)keyObj).getName(); } else if (keyObj.getClass() == CameraCharacteristics.Key.class) { return ((CameraCharacteristics.Key)keyObj).getName(); } throw new ItsException("Invalid key object"); } private static Object getKeyValue(CameraMetadata md, Object keyObj) throws ItsException { if (md.getClass() == CaptureResult.class || md.getClass() == TotalCaptureResult.class) { return ((CaptureResult)md).get((CaptureResult.Key)keyObj); } else if (md.getClass() == CaptureRequest.class) { return ((CaptureRequest)md).get((CaptureRequest.Key)keyObj); } else if (md.getClass() == CameraCharacteristics.class) { return ((CameraCharacteristics)md).get((CameraCharacteristics.Key)keyObj); } throw new ItsException("Invalid key object"); } @SuppressWarnings("unchecked") private static MetadataEntry serializeEntry(Type keyType, Object keyObj, CameraMetadata md) throws ItsException { String keyName = getKeyName(keyObj); try { Object keyValue = getKeyValue(md, keyObj); if (keyValue == null) { return new MetadataEntry(keyName, JSONObject.NULL); } else if (keyType == Float.class) { // The JSON serializer doesn't handle floating point NaN or Inf. if (((Float)keyValue).isInfinite() || ((Float)keyValue).isNaN()) { Logt.w(TAG, "Inf/NaN floating point value serialized: " + keyName); return null; } return new MetadataEntry(keyName, keyValue); } else if (keyType == Integer.class || keyType == Long.class || keyType == Byte.class || keyType == Boolean.class || keyType == String.class) { return new MetadataEntry(keyName, keyValue); } else if (keyType == Rational.class) { return new MetadataEntry(keyName, serializeRational((Rational)keyValue)); } else if (keyType == Size.class) { return new MetadataEntry(keyName, serializeSize((Size)keyValue)); } else if (keyType == SizeF.class) { return new MetadataEntry(keyName, serializeSizeF((SizeF)keyValue)); } else if (keyType == Rect.class) { return new MetadataEntry(keyName, serializeRect((Rect)keyValue)); } else if (keyType == Face.class) { return new MetadataEntry(keyName, serializeFace((Face)keyValue)); } else if (keyType == StreamConfigurationMap.class) { return new MetadataEntry(keyName, serializeStreamConfigurationMap((StreamConfigurationMap)keyValue)); } else if (keyType instanceof ParameterizedType && ((ParameterizedType)keyType).getRawType() == Range.class) { return new MetadataEntry(keyName, serializeRange((Range)keyValue)); } else if (keyType == ColorSpaceTransform.class) { return new MetadataEntry(keyName, serializeColorSpaceTransform((ColorSpaceTransform)keyValue)); } else if (keyType == MeteringRectangle.class) { return new MetadataEntry(keyName, serializeMeteringRectangle((MeteringRectangle)keyValue)); } else if (keyType == Location.class) { return new MetadataEntry(keyName, serializeLocation((Location)keyValue)); } else if (keyType == RggbChannelVector.class) { return new MetadataEntry(keyName, serializeRggbChannelVector((RggbChannelVector)keyValue)); } else if (keyType == BlackLevelPattern.class) { return new MetadataEntry(keyName, serializeBlackLevelPattern((BlackLevelPattern)keyValue)); } else if (keyType == TonemapCurve.class) { return new MetadataEntry(keyName, serializeTonemapCurve((TonemapCurve)keyValue)); } else if (keyType == Point.class) { return new MetadataEntry(keyName, serializePoint((Point)keyValue)); } else if (keyType == LensShadingMap.class) { return new MetadataEntry(keyName, serializeLensShadingMap((LensShadingMap)keyValue)); } else { Logt.w(TAG, String.format("Serializing unsupported key type: " + keyType)); return null; } } catch (org.json.JSONException e) { throw new ItsException("JSON error for key: " + keyName + ": ", e); } } @SuppressWarnings("unchecked") private static MetadataEntry serializeArrayEntry(Type keyType, Object keyObj, CameraMetadata md) throws ItsException { String keyName = getKeyName(keyObj); try { Object keyValue = getKeyValue(md, keyObj); if (keyValue == null) { return new MetadataEntry(keyName, JSONObject.NULL); } int arrayLen = Array.getLength(keyValue); Type elmtType = ((GenericArrayType)keyType).getGenericComponentType(); if (elmtType == int.class || elmtType == float.class || elmtType == byte.class || elmtType == long.class || elmtType == double.class || elmtType == boolean.class) { return new MetadataEntry(keyName, new JSONArray(keyValue)); } else if (elmtType == Rational.class) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < arrayLen; i++) { jsonArray.put(serializeRational((Rational)Array.get(keyValue,i))); } return new MetadataEntry(keyName, jsonArray); } else if (elmtType == Size.class) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < arrayLen; i++) { jsonArray.put(serializeSize((Size)Array.get(keyValue,i))); } return new MetadataEntry(keyName, jsonArray); } else if (elmtType == Rect.class) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < arrayLen; i++) { jsonArray.put(serializeRect((Rect)Array.get(keyValue,i))); } return new MetadataEntry(keyName, jsonArray); } else if (elmtType == Face.class) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < arrayLen; i++) { jsonArray.put(serializeFace((Face)Array.get(keyValue, i))); } return new MetadataEntry(keyName, jsonArray); } else if (elmtType == StreamConfigurationMap.class) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < arrayLen; i++) { jsonArray.put(serializeStreamConfigurationMap( (StreamConfigurationMap)Array.get(keyValue,i))); } return new MetadataEntry(keyName, jsonArray); } else if (elmtType instanceof ParameterizedType && ((ParameterizedType)elmtType).getRawType() == Range.class) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < arrayLen; i++) { jsonArray.put(serializeRange((Range)Array.get(keyValue,i))); } return new MetadataEntry(keyName, jsonArray); } else if (elmtType instanceof ParameterizedType && ((ParameterizedType)elmtType).getRawType() == Pair.class) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < arrayLen; i++) { jsonArray.put(serializePair((Pair)Array.get(keyValue,i))); } return new MetadataEntry(keyName, jsonArray); } else if (elmtType == MeteringRectangle.class) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < arrayLen; i++) { jsonArray.put(serializeMeteringRectangle( (MeteringRectangle)Array.get(keyValue,i))); } return new MetadataEntry(keyName, jsonArray); } else if (elmtType == Location.class) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < arrayLen; i++) { jsonArray.put(serializeLocation((Location)Array.get(keyValue,i))); } return new MetadataEntry(keyName, jsonArray); } else if (elmtType == RggbChannelVector.class) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < arrayLen; i++) { jsonArray.put(serializeRggbChannelVector( (RggbChannelVector)Array.get(keyValue,i))); } return new MetadataEntry(keyName, jsonArray); } else if (elmtType == BlackLevelPattern.class) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < arrayLen; i++) { jsonArray.put(serializeBlackLevelPattern( (BlackLevelPattern)Array.get(keyValue,i))); } return new MetadataEntry(keyName, jsonArray); } else if (elmtType == Point.class) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < arrayLen; i++) { jsonArray.put(serializePoint((Point)Array.get(keyValue,i))); } return new MetadataEntry(keyName, jsonArray); } else { Logt.w(TAG, String.format("Serializing unsupported array type: " + elmtType)); return null; } } catch (org.json.JSONException e) { throw new ItsException("JSON error for key: " + keyName + ": ", e); } } @SuppressWarnings("unchecked") public static JSONObject serialize(CameraMetadata md) throws ItsException { JSONObject jsonObj = new JSONObject(); Field[] allFields = md.getClass().getDeclaredFields(); if (md.getClass() == TotalCaptureResult.class) { allFields = CaptureResult.class.getDeclaredFields(); } for (Field field : allFields) { if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers()) && (field.getType() == CaptureRequest.Key.class || field.getType() == CaptureResult.Key.class || field.getType() == TotalCaptureResult.Key.class || field.getType() == CameraCharacteristics.Key.class) && field.getGenericType() instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType)field.getGenericType(); Type[] argTypes = paramType.getActualTypeArguments(); if (argTypes.length > 0) { try { Type keyType = argTypes[0]; Object keyObj = field.get(md); MetadataEntry entry; if (keyType instanceof GenericArrayType) { entry = serializeArrayEntry(keyType, keyObj, md); } else { entry = serializeEntry(keyType, keyObj, md); } // TODO: Figure this weird case out. // There is a weird case where the entry is non-null but the toString // of the entry is null, and if this happens, the null-ness spreads like // a virus and makes the whole JSON object null from the top level down. // Not sure if it's a bug in the library or I'm just not using it right. // Workaround by checking for this case explicitly and not adding the // value to the jsonObj when it is detected. if (entry != null && entry.key != null && entry.value != null && entry.value.toString() == null) { Logt.w(TAG, "Error encountered serializing value for key: " + entry.key); } else if (entry != null) { jsonObj.put(entry.key, entry.value); } else { // Ignore. } } catch (IllegalAccessException e) { throw new ItsException( "Access error for field: " + field + ": ", e); } catch (org.json.JSONException e) { throw new ItsException( "JSON error for field: " + field + ": ", e); } } } } return jsonObj; } @SuppressWarnings("unchecked") public static CaptureRequest.Builder deserialize(CaptureRequest.Builder mdDefault, JSONObject jsonReq) throws ItsException { try { Logt.i(TAG, "Parsing JSON capture request ..."); // Iterate over the CaptureRequest reflected fields. CaptureRequest.Builder md = mdDefault; Field[] allFields = CaptureRequest.class.getDeclaredFields(); for (Field field : allFields) { if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers()) && field.getType() == CaptureRequest.Key.class && field.getGenericType() instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType)field.getGenericType(); Type[] argTypes = paramType.getActualTypeArguments(); if (argTypes.length > 0) { CaptureRequest.Key key = (CaptureRequest.Key)field.get(md); String keyName = key.getName(); Type keyType = argTypes[0]; // For each reflected CaptureRequest entry, look inside the JSON object // to see if it is being set. If it is found, remove the key from the // JSON object. After this process, there should be no keys left in the // JSON (otherwise an invalid key was specified). if (jsonReq.has(keyName) && !jsonReq.isNull(keyName)) { if (keyType instanceof GenericArrayType) { Type elmtType = ((GenericArrayType)keyType).getGenericComponentType(); JSONArray ja = jsonReq.getJSONArray(keyName); Object val[] = new Object[ja.length()]; for (int i = 0; i < ja.length(); i++) { if (elmtType == int.class) { Array.set(val, i, ja.getInt(i)); } else if (elmtType == byte.class) { Array.set(val, i, (byte)ja.getInt(i)); } else if (elmtType == float.class) { Array.set(val, i, (float)ja.getDouble(i)); } else if (elmtType == long.class) { Array.set(val, i, ja.getLong(i)); } else if (elmtType == double.class) { Array.set(val, i, ja.getDouble(i)); } else if (elmtType == boolean.class) { Array.set(val, i, ja.getBoolean(i)); } else if (elmtType == String.class) { Array.set(val, i, ja.getString(i)); } else if (elmtType == Size.class){ JSONObject obj = ja.getJSONObject(i); Array.set(val, i, new Size( obj.getInt("width"), obj.getInt("height"))); } else if (elmtType == Rect.class) { JSONObject obj = ja.getJSONObject(i); Array.set(val, i, new Rect( obj.getInt("left"), obj.getInt("top"), obj.getInt("bottom"), obj.getInt("right"))); } else if (elmtType == Rational.class) { JSONObject obj = ja.getJSONObject(i); Array.set(val, i, new Rational( obj.getInt("numerator"), obj.getInt("denominator"))); } else if (elmtType == RggbChannelVector.class) { JSONArray arr = ja.getJSONArray(i); Array.set(val, i, new RggbChannelVector( (float)arr.getDouble(0), (float)arr.getDouble(1), (float)arr.getDouble(2), (float)arr.getDouble(3))); } else if (elmtType == ColorSpaceTransform.class) { JSONArray arr = ja.getJSONArray(i); Rational xform[] = new Rational[9]; for (int j = 0; j < 9; j++) { xform[j] = new Rational( arr.getJSONObject(j).getInt("numerator"), arr.getJSONObject(j).getInt("denominator")); } Array.set(val, i, new ColorSpaceTransform(xform)); } else if (elmtType == MeteringRectangle.class) { JSONObject obj = ja.getJSONObject(i); Array.set(val, i, new MeteringRectangle( obj.getInt("x"), obj.getInt("y"), obj.getInt("width"), obj.getInt("height"), obj.getInt("weight"))); } else { throw new ItsException( "Failed to parse key from JSON: " + keyName); } } if (val != null) { Logt.i(TAG, "Set: "+keyName+" -> "+Arrays.toString(val)); md.set(key, val); jsonReq.remove(keyName); } } else { Object val = null; if (keyType == Integer.class) { val = jsonReq.getInt(keyName); } else if (keyType == Byte.class) { val = (byte)jsonReq.getInt(keyName); } else if (keyType == Double.class) { val = jsonReq.getDouble(keyName); } else if (keyType == Long.class) { val = jsonReq.getLong(keyName); } else if (keyType == Float.class) { val = (float)jsonReq.getDouble(keyName); } else if (keyType == Boolean.class) { val = jsonReq.getBoolean(keyName); } else if (keyType == String.class) { val = jsonReq.getString(keyName); } else if (keyType == Size.class) { JSONObject obj = jsonReq.getJSONObject(keyName); val = new Size( obj.getInt("width"), obj.getInt("height")); } else if (keyType == Rect.class) { JSONObject obj = jsonReq.getJSONObject(keyName); val = new Rect( obj.getInt("left"), obj.getInt("top"), obj.getInt("right"), obj.getInt("bottom")); } else if (keyType == Rational.class) { JSONObject obj = jsonReq.getJSONObject(keyName); val = new Rational(obj.getInt("numerator"), obj.getInt("denominator")); } else if (keyType == RggbChannelVector.class) { JSONObject obj = jsonReq.optJSONObject(keyName); JSONArray arr = jsonReq.optJSONArray(keyName); if (arr != null) { val = new RggbChannelVector( (float)arr.getDouble(0), (float)arr.getDouble(1), (float)arr.getDouble(2), (float)arr.getDouble(3)); } else if (obj != null) { val = new RggbChannelVector( (float)obj.getDouble("red"), (float)obj.getDouble("greenEven"), (float)obj.getDouble("greenOdd"), (float)obj.getDouble("blue")); } else { throw new ItsException("Invalid RggbChannelVector object"); } } else if (keyType == ColorSpaceTransform.class) { JSONArray arr = jsonReq.getJSONArray(keyName); Rational a[] = new Rational[9]; for (int i = 0; i < 9; i++) { a[i] = new Rational( arr.getJSONObject(i).getInt("numerator"), arr.getJSONObject(i).getInt("denominator")); } val = new ColorSpaceTransform(a); } else if (keyType instanceof ParameterizedType && ((ParameterizedType)keyType).getRawType() == Range.class && ((ParameterizedType)keyType).getActualTypeArguments().length == 1 && ((ParameterizedType)keyType).getActualTypeArguments()[0] == Integer.class) { JSONArray arr = jsonReq.getJSONArray(keyName); val = new Range(arr.getInt(0), arr.getInt(1)); } else { throw new ItsException( "Failed to parse key from JSON: " + keyName + ", " + keyType); } if (val != null) { Logt.i(TAG, "Set: " + keyName + " -> " + val); md.set(key ,val); jsonReq.remove(keyName); } } } } } } // Ensure that there were no invalid keys in the JSON request object. if (jsonReq.length() != 0) { throw new ItsException("Invalid JSON key(s): " + jsonReq.toString()); } Logt.i(TAG, "Parsing JSON capture request completed"); return md; } catch (java.lang.IllegalAccessException e) { throw new ItsException("Access error: ", e); } catch (org.json.JSONException e) { throw new ItsException("JSON error: ", e); } } @SuppressWarnings("unchecked") public static List deserializeRequestList( CameraDevice device, JSONObject jsonObjTop) throws ItsException { try { List requests = null; JSONArray jsonReqs = jsonObjTop.getJSONArray("captureRequests"); requests = new LinkedList(); for (int i = 0; i < jsonReqs.length(); i++) { CaptureRequest.Builder templateReq = device.createCaptureRequest( CameraDevice.TEMPLATE_STILL_CAPTURE); requests.add( deserialize(templateReq, jsonReqs.getJSONObject(i))); } return requests; } catch (org.json.JSONException e) { throw new ItsException("JSON error: ", e); } catch (android.hardware.camera2.CameraAccessException e) { throw new ItsException("Access error: ", e); } } }