1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.camera2.its; 18 19 import android.hardware.camera2.CameraDevice; 20 import android.hardware.camera2.CameraMetadata; 21 import android.hardware.camera2.CaptureRequest; 22 import android.hardware.camera2.Rational; 23 import android.util.Log; 24 25 import org.json.JSONArray; 26 import org.json.JSONObject; 27 28 import java.lang.reflect.Array; 29 import java.lang.reflect.Field; 30 import java.lang.reflect.GenericArrayType; 31 import java.lang.reflect.Modifier; 32 import java.lang.reflect.ParameterizedType; 33 import java.lang.reflect.Type; 34 import java.util.Arrays; 35 import java.util.LinkedList; 36 import java.util.List; 37 38 /** 39 * Class to deal with serializing and deserializing between JSON and Camera2 objects. 40 */ 41 public class ItsSerializer { 42 public static final String TAG = ItsSerializer.class.getSimpleName(); 43 44 private static class MetadataEntry { 45 public MetadataEntry(String k, Object v) { 46 key = k; 47 value = v; 48 } 49 public String key; 50 public Object value; 51 } 52 53 @SuppressWarnings("unchecked") 54 private static MetadataEntry serializeEntry(Type keyType, Object keyObj, CameraMetadata md) 55 throws ItsException { 56 CameraMetadata.Key key = (CameraMetadata.Key)keyObj; 57 try { 58 if (md.get(key) == null) { 59 return new MetadataEntry(key.getName(), JSONObject.NULL); 60 } 61 if (keyType == Integer.class || keyType == Long.class || keyType == Byte.class || 62 keyType == Float.class || keyType == Boolean.class || keyType == String.class) { 63 return new MetadataEntry(key.getName(), md.get(key)); 64 } else if (keyType == Rational.class) { 65 CameraMetadata.Key<Rational> key2 = (CameraMetadata.Key<Rational>)keyObj; 66 JSONObject ratObj = new JSONObject(); 67 ratObj.put("numerator", md.get(key2).getNumerator()); 68 ratObj.put("denominator", md.get(key2).getDenominator()); 69 return new MetadataEntry(key.getName(), ratObj); 70 } else if (keyType == android.hardware.camera2.Size.class) { 71 CameraMetadata.Key<android.hardware.camera2.Size> key2 = 72 (CameraMetadata.Key<android.hardware.camera2.Size>)keyObj; 73 JSONObject sizeObj = new JSONObject(); 74 sizeObj.put("width", md.get(key2).getWidth()); 75 sizeObj.put("height", md.get(key2).getHeight()); 76 return new MetadataEntry(key.getName(), sizeObj); 77 } else if (keyType == android.graphics.Rect.class) { 78 CameraMetadata.Key<android.graphics.Rect> key2 = 79 (CameraMetadata.Key<android.graphics.Rect>)keyObj; 80 JSONObject rectObj = new JSONObject(); 81 rectObj.put("left", md.get(key2).left); 82 rectObj.put("right", md.get(key2).right); 83 rectObj.put("top", md.get(key2).top); 84 rectObj.put("bottom", md.get(key2).bottom); 85 return new MetadataEntry(key.getName(), rectObj); 86 } else { 87 throw new ItsException( 88 "Unsupported key type in metadata serializer: " + keyType); 89 } 90 } catch (org.json.JSONException e) { 91 throw new ItsException("JSON error for key: " + key.getName() + ": ", e); 92 } 93 } 94 95 @SuppressWarnings("unchecked") 96 private static MetadataEntry serializeArrayEntry(Type keyType, Object keyObj, CameraMetadata md) 97 throws ItsException { 98 CameraMetadata.Key key = (CameraMetadata.Key)keyObj; 99 try { 100 if (md.get(key) == null) { 101 return new MetadataEntry(key.getName(), JSONObject.NULL); 102 } 103 Type elmtType = ((GenericArrayType)keyType).getGenericComponentType(); 104 if (elmtType == int.class || elmtType == float.class || elmtType == byte.class || 105 elmtType == long.class || elmtType == double.class) { 106 return new MetadataEntry(key.getName(), new JSONArray(md.get(key))); 107 } else if (elmtType == Rational.class) { 108 CameraMetadata.Key<Rational[]> key2 = (CameraMetadata.Key<Rational[]>)keyObj; 109 JSONArray jsonArray = new JSONArray(); 110 for (int i = 0; i < Array.getLength(md.get(key)); i++) { 111 JSONObject ratObj = new JSONObject(); 112 ratObj.put("numerator", md.get(key2)[i].getNumerator()); 113 ratObj.put("denominator", md.get(key2)[i].getDenominator()); 114 jsonArray.put(ratObj); 115 } 116 return new MetadataEntry(key.getName(), jsonArray); 117 } else if (elmtType == android.hardware.camera2.Size.class) { 118 CameraMetadata.Key<android.hardware.camera2.Size[]> key2 = 119 (CameraMetadata.Key<android.hardware.camera2.Size[]>)keyObj; 120 JSONArray jsonArray = new JSONArray(); 121 for (int i = 0; i < Array.getLength(md.get(key)); i++) { 122 JSONObject sizeObj = new JSONObject(); 123 sizeObj.put("width", md.get(key2)[i].getWidth()); 124 sizeObj.put("height", md.get(key2)[i].getHeight()); 125 jsonArray.put(sizeObj); 126 } 127 return new MetadataEntry(key.getName(), jsonArray); 128 } else if (elmtType == android.graphics.Rect.class) { 129 CameraMetadata.Key<android.graphics.Rect[]> key2 = 130 (CameraMetadata.Key<android.graphics.Rect[]>)keyObj; 131 JSONArray jsonArray = new JSONArray(); 132 for (int i = 0; i < Array.getLength(md.get(key)); i++) { 133 JSONObject rectObj = new JSONObject(); 134 rectObj.put("left", md.get(key2)[i].left); 135 rectObj.put("right", md.get(key2)[i].right); 136 rectObj.put("top", md.get(key2)[i].top); 137 rectObj.put("bottom", md.get(key2)[i].bottom); 138 jsonArray.put(rectObj); 139 } 140 return new MetadataEntry(key.getName(), jsonArray); 141 } else if (elmtType == android.hardware.camera2.Face.class) { 142 CameraMetadata.Key<android.hardware.camera2.Face[]> key2 = 143 (CameraMetadata.Key<android.hardware.camera2.Face[]>)keyObj; 144 145 // TODO: Serialize an array of faces to JSON. 146 // Will also need to deserialize JSON faces in the appropriate method. 147 if (Array.getLength(md.get(key)) != 0) { 148 throw new ItsException("Serialization of faces not implemented yet"); 149 } 150 return new MetadataEntry(key.getName(), new JSONArray()); 151 152 } else { 153 throw new ItsException("Unsupported array type: " + elmtType); 154 } 155 } catch (org.json.JSONException e) { 156 throw new ItsException("JSON error for key: " + key.getName() + ": ", e); 157 } 158 } 159 160 @SuppressWarnings("unchecked") 161 public static JSONObject serialize(CameraMetadata md) 162 throws ItsException { 163 JSONObject jsonObj = new JSONObject(); 164 Field[] allFields = md.getClass().getDeclaredFields(); 165 for (Field field : allFields) { 166 if (Modifier.isPublic(field.getModifiers()) && 167 Modifier.isStatic(field.getModifiers()) && 168 field.getType() == CameraMetadata.Key.class && 169 field.getGenericType() instanceof ParameterizedType) { 170 ParameterizedType paramType = (ParameterizedType)field.getGenericType(); 171 Type[] argTypes = paramType.getActualTypeArguments(); 172 if (argTypes.length > 0) { 173 try { 174 Type keyType = argTypes[0]; 175 Object keyObj = field.get(md); 176 MetadataEntry entry; 177 if (keyType instanceof GenericArrayType) { 178 entry = serializeArrayEntry(keyType, keyObj, md); 179 } else { 180 entry = serializeEntry(keyType, keyObj, md); 181 } 182 if (entry != null) { 183 jsonObj.put(entry.key, entry.value); 184 } 185 } catch (IllegalAccessException e) { 186 throw new ItsException( 187 "Access error for field: " + field + ": ", e); 188 } catch (org.json.JSONException e) { 189 throw new ItsException( 190 "JSON error for field: " + field + ": ", e); 191 } catch (IllegalArgumentException e) { 192 // TODO: Remove this HACK once all keys are implemented (especially 193 // android.statistics.faces). 194 // If the key isn't plumbed all the way down, can get an exception by 195 // trying to get it. Swallow the exception. 196 } 197 } 198 } 199 } 200 return jsonObj; 201 } 202 203 @SuppressWarnings("unchecked") 204 public static CaptureRequest.Builder deserialize(CaptureRequest.Builder mdDefault, 205 JSONObject jsonReq) throws ItsException { 206 try { 207 Log.i(TAG, "Parsing JSON capture request ..."); 208 209 // Iterate over the CameraMetadata reflected fields. 210 CaptureRequest.Builder md = mdDefault; 211 Field[] allFields = CaptureRequest.class.getDeclaredFields(); 212 for (Field field : allFields) { 213 if (Modifier.isPublic(field.getModifiers()) && 214 Modifier.isStatic(field.getModifiers()) && 215 field.getType() == CameraMetadata.Key.class && 216 field.getGenericType() instanceof ParameterizedType) { 217 ParameterizedType paramType = (ParameterizedType)field.getGenericType(); 218 Type[] argTypes = paramType.getActualTypeArguments(); 219 try { 220 if (argTypes.length > 0) { 221 CameraMetadata.Key key = (CameraMetadata.Key)field.get(md); 222 String keyName = key.getName(); 223 Type keyType = argTypes[0]; 224 225 // For each reflected CameraMetadata entry, look inside the JSON object 226 // to see if it is being set. If it is found, remove the key from the 227 // JSON object. After this process, there should be no keys left in the 228 // JSON (otherwise an invalid key was specified). 229 230 if (jsonReq.has(keyName) && !jsonReq.isNull(keyName)) { 231 if (keyType instanceof GenericArrayType) { 232 Type elmtType = 233 ((GenericArrayType)keyType).getGenericComponentType(); 234 JSONArray ja = jsonReq.getJSONArray(keyName); 235 Object val[] = new Object[ja.length()]; 236 for (int i = 0; i < ja.length(); i++) { 237 if (elmtType == int.class) { 238 Array.set(val, i, ja.getInt(i)); 239 } else if (elmtType == byte.class) { 240 Array.set(val, i, (byte)ja.getInt(i)); 241 } else if (elmtType == float.class) { 242 Array.set(val, i, (float)ja.getDouble(i)); 243 } else if (elmtType == long.class) { 244 Array.set(val, i, ja.getLong(i)); 245 } else if (elmtType == double.class) { 246 Array.set(val, i, ja.getDouble(i)); 247 } else if (elmtType == boolean.class) { 248 Array.set(val, i, ja.getBoolean(i)); 249 } else if (elmtType == String.class) { 250 Array.set(val, i, ja.getString(i)); 251 } else if (elmtType == android.hardware.camera2.Size.class){ 252 JSONObject obj = ja.getJSONObject(i); 253 Array.set(val, i, new android.hardware.camera2.Size( 254 obj.getInt("width"), obj.getInt("height"))); 255 } else if (elmtType == android.graphics.Rect.class) { 256 JSONObject obj = ja.getJSONObject(i); 257 Array.set(val, i, new android.graphics.Rect( 258 obj.getInt("left"), obj.getInt("top"), 259 obj.getInt("bottom"), obj.getInt("right"))); 260 } else if (elmtType == Rational.class) { 261 JSONObject obj = ja.getJSONObject(i); 262 Array.set(val, i, new Rational( 263 obj.getInt("numerator"), 264 obj.getInt("denominator"))); 265 } else { 266 throw new ItsException( 267 "Failed to parse key from JSON: " + keyName); 268 } 269 } 270 if (val != null) { 271 Log.i(TAG, "Set: "+keyName+" -> "+Arrays.toString(val)); 272 md.set(key, val); 273 jsonReq.remove(keyName); 274 } 275 } else { 276 Object val = null; 277 if (keyType == Integer.class) { 278 val = jsonReq.getInt(keyName); 279 } else if (keyType == Byte.class) { 280 val = (byte)jsonReq.getInt(keyName); 281 } else if (keyType == Double.class) { 282 val = jsonReq.getDouble(keyName); 283 } else if (keyType == Long.class) { 284 val = jsonReq.getLong(keyName); 285 } else if (keyType == Float.class) { 286 val = (float)jsonReq.getDouble(keyName); 287 } else if (keyType == Boolean.class) { 288 val = jsonReq.getBoolean(keyName); 289 } else if (keyType == String.class) { 290 val = jsonReq.getString(keyName); 291 } else if (keyType == android.hardware.camera2.Size.class) { 292 JSONObject obj = jsonReq.getJSONObject(keyName); 293 val = new android.hardware.camera2.Size( 294 obj.getInt("width"), obj.getInt("height")); 295 } else if (keyType == android.graphics.Rect.class) { 296 JSONObject obj = jsonReq.getJSONObject(keyName); 297 val = new android.graphics.Rect( 298 obj.getInt("left"), obj.getInt("top"), 299 obj.getInt("right"), obj.getInt("bottom")); 300 } else if (keyType == Rational.class) { 301 JSONObject obj = jsonReq.getJSONObject(keyName); 302 val = new Rational(obj.getInt("numerator"), 303 obj.getInt("denominator")); 304 } else { 305 throw new ItsException( 306 "Failed to parse key from JSON: " + keyName); 307 } 308 if (val != null) { 309 Log.i(TAG, "Set: " + keyName + " -> " + val); 310 md.set(key ,val); 311 jsonReq.remove(keyName); 312 } 313 } 314 } 315 } 316 } catch (IllegalArgumentException e) { 317 // TODO: Remove this HACK once all keys are implemented (especially 318 // android.statistics.faces). 319 // If the key isn't plumbed all the way down, can get an exception by 320 // trying to get it. Swallow the exception. 321 } 322 } 323 } 324 325 // Ensure that there were no invalid keys in the JSON request object. 326 if (jsonReq.length() != 0) { 327 throw new ItsException("Invalid JSON key(s): " + jsonReq.toString()); 328 } 329 330 Log.i(TAG, "Parsing JSON capture request completed"); 331 return md; 332 } catch (java.lang.IllegalAccessException e) { 333 throw new ItsException("Access error: ", e); 334 } catch (org.json.JSONException e) { 335 throw new ItsException("JSON error: ", e); 336 } 337 } 338 339 @SuppressWarnings("unchecked") 340 public static List<CaptureRequest.Builder> deserializeRequestList( 341 CameraDevice device, JSONObject jsonObjTop) 342 throws ItsException { 343 try { 344 List<CaptureRequest.Builder> requests = null; 345 JSONArray jsonReqs = jsonObjTop.getJSONArray("captureRequests"); 346 requests = new LinkedList<CaptureRequest.Builder>(); 347 for (int i = 0; i < jsonReqs.length(); i++) { 348 CaptureRequest.Builder templateReq = device.createCaptureRequest( 349 CameraDevice.TEMPLATE_STILL_CAPTURE); 350 requests.add( 351 deserialize(templateReq, jsonReqs.getJSONObject(i))); 352 } 353 return requests; 354 } catch (org.json.JSONException e) { 355 throw new ItsException("JSON error: ", e); 356 } catch (android.hardware.camera2.CameraAccessException e) { 357 throw new ItsException("Access error: ", e); 358 } 359 } 360 } 361