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.CaptureRequest; 25 import android.hardware.camera2.CaptureResult; 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.LensIntrinsicsSample; 31 import android.hardware.camera2.params.LensShadingMap; 32 import android.hardware.camera2.params.MeteringRectangle; 33 import android.hardware.camera2.params.RggbChannelVector; 34 import android.hardware.camera2.params.StreamConfigurationMap; 35 import android.hardware.camera2.params.TonemapCurve; 36 import android.location.Location; 37 import android.util.Pair; 38 import android.util.Range; 39 import android.util.Rational; 40 import android.util.Size; 41 import android.util.SizeF; 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 281 @SuppressWarnings("unchecked") serializeIntrinsicsSamples(LensIntrinsicsSample [] samples)282 private static Object serializeIntrinsicsSamples(LensIntrinsicsSample [] samples) 283 throws org.json.JSONException { 284 JSONArray top = new JSONArray(); 285 for (LensIntrinsicsSample sample : samples) { 286 JSONObject jSample = new JSONObject(); 287 jSample.put("timestamp", sample.getTimestampNanos()); 288 JSONArray lensIntrinsics = new JSONArray(); 289 for (float intrinsic : sample.getLensIntrinsics()) { 290 lensIntrinsics.put(intrinsic); 291 } 292 jSample.put("lensIntrinsics", lensIntrinsics); 293 top.put(jSample); 294 } 295 return top; 296 } getKeyName(Object keyObj)297 private static String getKeyName(Object keyObj) throws ItsException { 298 if (keyObj.getClass() == CaptureResult.Key.class 299 || keyObj.getClass() == TotalCaptureResult.class) { 300 return ((CaptureResult.Key)keyObj).getName(); 301 } else if (keyObj.getClass() == CaptureRequest.Key.class) { 302 return ((CaptureRequest.Key)keyObj).getName(); 303 } else if (keyObj.getClass() == CameraCharacteristics.Key.class) { 304 return ((CameraCharacteristics.Key)keyObj).getName(); 305 } 306 throw new ItsException("Invalid key object"); 307 } 308 getKeyValue(CameraMetadata md, Object keyObj)309 private static Object getKeyValue(CameraMetadata md, Object keyObj) throws ItsException { 310 if (md.getClass() == CaptureResult.class || md.getClass() == TotalCaptureResult.class) { 311 return ((CaptureResult)md).get((CaptureResult.Key)keyObj); 312 } else if (md.getClass() == CaptureRequest.class) { 313 return ((CaptureRequest)md).get((CaptureRequest.Key)keyObj); 314 } else if (md.getClass() == CameraCharacteristics.class) { 315 return ((CameraCharacteristics)md).get((CameraCharacteristics.Key)keyObj); 316 } 317 throw new ItsException("Invalid key object"); 318 } 319 serializeEntry(Type keyType, Object keyObj, CameraMetadata md)320 private static MetadataEntry serializeEntry(Type keyType, Object keyObj, CameraMetadata md) 321 throws ItsException { 322 return serializeEntry(keyType, keyObj, getKeyValue(md, keyObj)); 323 } 324 325 @SuppressWarnings("unchecked") serializeEntry(Type keyType, Object keyObj, Object keyValue)326 private static MetadataEntry serializeEntry(Type keyType, Object keyObj, Object keyValue) 327 throws ItsException { 328 String keyName = getKeyName(keyObj); 329 330 try { 331 if (keyValue == null) { 332 return new MetadataEntry(keyName, JSONObject.NULL); 333 } else if (keyType == Float.class) { 334 // The JSON serializer doesn't handle floating point NaN or Inf. 335 if (((Float)keyValue).isInfinite() || ((Float)keyValue).isNaN()) { 336 Logt.w(TAG, "Inf/NaN floating point value serialized: " + keyName); 337 return null; 338 } 339 return new MetadataEntry(keyName, keyValue); 340 } else if (keyType == Integer.class || keyType == Long.class || keyType == Byte.class || 341 keyType == Boolean.class || keyType == String.class) { 342 return new MetadataEntry(keyName, keyValue); 343 } else if (keyType == Rational.class) { 344 return new MetadataEntry(keyName, serializeRational((Rational)keyValue)); 345 } else if (keyType == Size.class) { 346 return new MetadataEntry(keyName, serializeSize((Size)keyValue)); 347 } else if (keyType == SizeF.class) { 348 return new MetadataEntry(keyName, serializeSizeF((SizeF)keyValue)); 349 } else if (keyType == Rect.class) { 350 return new MetadataEntry(keyName, serializeRect((Rect)keyValue)); 351 } else if (keyType == Face.class) { 352 return new MetadataEntry(keyName, serializeFace((Face)keyValue)); 353 } else if (keyType == StreamConfigurationMap.class) { 354 return new MetadataEntry(keyName, 355 serializeStreamConfigurationMap((StreamConfigurationMap)keyValue)); 356 } else if (keyType instanceof ParameterizedType && 357 ((ParameterizedType)keyType).getRawType() == Range.class) { 358 return new MetadataEntry(keyName, serializeRange((Range)keyValue)); 359 } else if (keyType == ColorSpaceTransform.class) { 360 return new MetadataEntry(keyName, 361 serializeColorSpaceTransform((ColorSpaceTransform)keyValue)); 362 } else if (keyType == MeteringRectangle.class) { 363 return new MetadataEntry(keyName, 364 serializeMeteringRectangle((MeteringRectangle)keyValue)); 365 } else if (keyType == Location.class) { 366 return new MetadataEntry(keyName, 367 serializeLocation((Location)keyValue)); 368 } else if (keyType == RggbChannelVector.class) { 369 return new MetadataEntry(keyName, 370 serializeRggbChannelVector((RggbChannelVector)keyValue)); 371 } else if (keyType == BlackLevelPattern.class) { 372 return new MetadataEntry(keyName, 373 serializeBlackLevelPattern((BlackLevelPattern)keyValue)); 374 } else if (keyType == TonemapCurve.class) { 375 return new MetadataEntry(keyName, 376 serializeTonemapCurve((TonemapCurve)keyValue)); 377 } else if (keyType == Point.class) { 378 return new MetadataEntry(keyName, 379 serializePoint((Point)keyValue)); 380 } else if (keyType == LensShadingMap.class) { 381 return new MetadataEntry(keyName, 382 serializeLensShadingMap((LensShadingMap)keyValue)); 383 } else if (keyValue instanceof LensIntrinsicsSample[]) { 384 return new MetadataEntry(keyName, 385 serializeIntrinsicsSamples((LensIntrinsicsSample []) keyValue)); 386 } else if (keyValue instanceof float[]) { 387 return new MetadataEntry(keyName, new JSONArray(keyValue)); 388 } else { 389 Logt.w(TAG, String.format("Serializing unsupported key type: " + keyType)); 390 return null; 391 } 392 } catch (org.json.JSONException e) { 393 throw new ItsException("JSON error for key: " + keyName + ": ", e); 394 } 395 } 396 serializeArrayEntry(Type keyType, Object keyObj, CameraMetadata md)397 private static MetadataEntry serializeArrayEntry(Type keyType, Object keyObj, CameraMetadata md) 398 throws ItsException { 399 return serializeArrayEntry(keyType, keyObj, getKeyValue(md, keyObj)); 400 } 401 402 @SuppressWarnings("unchecked") serializeArrayEntry(Type keyType, Object keyObj, Object keyValue)403 private static MetadataEntry serializeArrayEntry(Type keyType, Object keyObj, Object keyValue) 404 throws ItsException { 405 String keyName = getKeyName(keyObj); 406 try { 407 if (keyValue == null) { 408 return null; 409 } 410 int arrayLen = Array.getLength(keyValue); 411 Type elmtType = ((GenericArrayType)keyType).getGenericComponentType(); 412 if (elmtType == int.class || elmtType == float.class || elmtType == byte.class || 413 elmtType == long.class || elmtType == double.class || elmtType == boolean.class) { 414 return new MetadataEntry(keyName, new JSONArray(keyValue)); 415 } else if (elmtType == Rational.class) { 416 JSONArray jsonArray = new JSONArray(); 417 for (int i = 0; i < arrayLen; i++) { 418 jsonArray.put(serializeRational((Rational)Array.get(keyValue,i))); 419 } 420 return new MetadataEntry(keyName, jsonArray); 421 } else if (elmtType == Size.class) { 422 JSONArray jsonArray = new JSONArray(); 423 for (int i = 0; i < arrayLen; i++) { 424 jsonArray.put(serializeSize((Size)Array.get(keyValue,i))); 425 } 426 return new MetadataEntry(keyName, jsonArray); 427 } else if (elmtType == Rect.class) { 428 JSONArray jsonArray = new JSONArray(); 429 for (int i = 0; i < arrayLen; i++) { 430 jsonArray.put(serializeRect((Rect)Array.get(keyValue,i))); 431 } 432 return new MetadataEntry(keyName, jsonArray); 433 } else if (elmtType == Face.class) { 434 JSONArray jsonArray = new JSONArray(); 435 for (int i = 0; i < arrayLen; i++) { 436 jsonArray.put(serializeFace((Face)Array.get(keyValue, i))); 437 } 438 return new MetadataEntry(keyName, jsonArray); 439 } else if (elmtType == StreamConfigurationMap.class) { 440 JSONArray jsonArray = new JSONArray(); 441 for (int i = 0; i < arrayLen; i++) { 442 jsonArray.put(serializeStreamConfigurationMap( 443 (StreamConfigurationMap)Array.get(keyValue,i))); 444 } 445 return new MetadataEntry(keyName, jsonArray); 446 } else if (elmtType instanceof ParameterizedType && 447 ((ParameterizedType)elmtType).getRawType() == Range.class) { 448 JSONArray jsonArray = new JSONArray(); 449 for (int i = 0; i < arrayLen; i++) { 450 jsonArray.put(serializeRange((Range)Array.get(keyValue,i))); 451 } 452 return new MetadataEntry(keyName, jsonArray); 453 } else if (elmtType instanceof ParameterizedType && 454 ((ParameterizedType)elmtType).getRawType() == Pair.class) { 455 JSONArray jsonArray = new JSONArray(); 456 for (int i = 0; i < arrayLen; i++) { 457 jsonArray.put(serializePair((Pair)Array.get(keyValue,i))); 458 } 459 return new MetadataEntry(keyName, jsonArray); 460 } else if (elmtType == MeteringRectangle.class) { 461 JSONArray jsonArray = new JSONArray(); 462 for (int i = 0; i < arrayLen; i++) { 463 jsonArray.put(serializeMeteringRectangle( 464 (MeteringRectangle)Array.get(keyValue,i))); 465 } 466 return new MetadataEntry(keyName, jsonArray); 467 } else if (elmtType == Location.class) { 468 JSONArray jsonArray = new JSONArray(); 469 for (int i = 0; i < arrayLen; i++) { 470 jsonArray.put(serializeLocation((Location)Array.get(keyValue,i))); 471 } 472 return new MetadataEntry(keyName, jsonArray); 473 } else if (elmtType == RggbChannelVector.class) { 474 JSONArray jsonArray = new JSONArray(); 475 for (int i = 0; i < arrayLen; i++) { 476 jsonArray.put(serializeRggbChannelVector( 477 (RggbChannelVector)Array.get(keyValue,i))); 478 } 479 return new MetadataEntry(keyName, jsonArray); 480 } else if (elmtType == BlackLevelPattern.class) { 481 JSONArray jsonArray = new JSONArray(); 482 for (int i = 0; i < arrayLen; i++) { 483 jsonArray.put(serializeBlackLevelPattern( 484 (BlackLevelPattern)Array.get(keyValue,i))); 485 } 486 return new MetadataEntry(keyName, jsonArray); 487 } else if (elmtType == Point.class) { 488 JSONArray jsonArray = new JSONArray(); 489 for (int i = 0; i < arrayLen; i++) { 490 jsonArray.put(serializePoint((Point)Array.get(keyValue,i))); 491 } 492 return new MetadataEntry(keyName, jsonArray); 493 } else { 494 Logt.w(TAG, String.format("Serializing unsupported array type: " + elmtType)); 495 return null; 496 } 497 } catch (org.json.JSONException e) { 498 throw new ItsException("JSON error for key: " + keyName + ": ", e); 499 } 500 } 501 serialize(RecordingResult recordingResult)502 public static JSONObject serialize(RecordingResult recordingResult) 503 throws ItsException { 504 JSONObject jsonObj = new JSONObject(); 505 for (CaptureResult.Key<?> key : recordingResult.getKeys()) { 506 Object value = recordingResult.getResult(key); 507 if (value == null) { 508 Logt.w(TAG, "Key value is null for: " + key.toString()); 509 continue; 510 } 511 Type keyType = value.getClass(); 512 MetadataEntry entry; 513 if (keyType instanceof GenericArrayType) { 514 entry = serializeArrayEntry(keyType, key, value); 515 } else { 516 entry = serializeEntry(keyType, key, value); 517 } 518 try { 519 if (entry != null) { 520 jsonObj.put(entry.key, entry.value); 521 } else { 522 Logt.w(TAG, "Key entry is null for: " + key.toString()); 523 } 524 } catch (org.json.JSONException e) { 525 throw new ItsException("JSON error for key: " + key.getName() + ": ", e); 526 } 527 } 528 return jsonObj; 529 } 530 531 @SuppressWarnings("unchecked") serialize(CameraMetadata md)532 public static JSONObject serialize(CameraMetadata md) 533 throws ItsException { 534 JSONObject jsonObj = new JSONObject(); 535 Field[] allFields = md.getClass().getDeclaredFields(); 536 if (md.getClass() == TotalCaptureResult.class) { 537 allFields = CaptureResult.class.getDeclaredFields(); 538 } 539 if (md.getClass() == CameraCharacteristics.class) { 540 // Special handling for information not stored in metadata keys 541 CameraCharacteristics chars = (CameraCharacteristics) md; 542 List<CameraCharacteristics.Key<?>> charsKeys = chars.getKeys(); 543 List<CaptureRequest.Key<?>> requestKeys = chars.getAvailableCaptureRequestKeys(); 544 List<CaptureResult.Key<?>> resultKeys = chars.getAvailableCaptureResultKeys(); 545 Set<String> physicalCamIds = chars.getPhysicalCameraIds(); 546 547 try { 548 JSONArray charKeysArr = new JSONArray(); 549 for (CameraCharacteristics.Key<?> k : charsKeys) { 550 charKeysArr.put(k.getName()); 551 } 552 JSONArray reqKeysArr = new JSONArray(); 553 for (CaptureRequest.Key<?> k : requestKeys) { 554 reqKeysArr.put(k.getName()); 555 } 556 JSONArray resKeysArr = new JSONArray(); 557 for (CaptureResult.Key<?> k : resultKeys) { 558 resKeysArr.put(k.getName()); 559 } 560 // Avoid using the hidden metadata key name here to prevent confliction 561 jsonObj.put("camera.characteristics.keys", charKeysArr); 562 jsonObj.put("camera.characteristics.requestKeys", reqKeysArr); 563 jsonObj.put("camera.characteristics.resultKeys", resKeysArr); 564 565 if (!physicalCamIds.isEmpty()) { 566 JSONArray physCamIdsArr = new JSONArray(); 567 for (String id : physicalCamIds) { 568 physCamIdsArr.put(id); 569 } 570 jsonObj.put("camera.characteristics.physicalCamIds", physCamIdsArr); 571 } 572 } catch (org.json.JSONException e) { 573 throw new ItsException("JSON error for CameraCharacteristics:", e); 574 } 575 } 576 for (Field field : allFields) { 577 if (Modifier.isPublic(field.getModifiers()) && 578 Modifier.isStatic(field.getModifiers()) && 579 (field.getType() == CaptureRequest.Key.class 580 || field.getType() == CaptureResult.Key.class 581 || field.getType() == TotalCaptureResult.Key.class 582 || field.getType() == CameraCharacteristics.Key.class) && 583 field.getGenericType() instanceof ParameterizedType) { 584 ParameterizedType paramType = (ParameterizedType)field.getGenericType(); 585 Type[] argTypes = paramType.getActualTypeArguments(); 586 if (argTypes.length > 0) { 587 try { 588 Type keyType = argTypes[0]; 589 Object keyObj = field.get(md); 590 MetadataEntry entry; 591 if (keyType instanceof GenericArrayType) { 592 entry = serializeArrayEntry(keyType, keyObj, md); 593 } else { 594 entry = serializeEntry(keyType, keyObj, md); 595 } 596 597 // TODO: Figure this weird case out. 598 // There is a weird case where the entry is non-null but the toString 599 // of the entry is null, and if this happens, the null-ness spreads like 600 // a virus and makes the whole JSON object null from the top level down. 601 // Not sure if it's a bug in the library or I'm just not using it right. 602 // Workaround by checking for this case explicitly and not adding the 603 // value to the jsonObj when it is detected. 604 if (entry != null && entry.key != null && entry.value != null 605 && entry.value.toString() == null) { 606 Logt.w(TAG, "Error encountered serializing value for key: " + entry.key); 607 } else if (entry != null) { 608 jsonObj.put(entry.key, entry.value); 609 } else { 610 // Ignore. 611 } 612 } catch (IllegalAccessException e) { 613 throw new ItsException( 614 "Access error for field: " + field + ": ", e); 615 } catch (org.json.JSONException e) { 616 throw new ItsException( 617 "JSON error for field: " + field + ": ", e); 618 } 619 } 620 } 621 } 622 return jsonObj; 623 } 624 625 @SuppressWarnings("unchecked") deserialize(CaptureRequest.Builder mdDefault, JSONObject jsonReq)626 public static CaptureRequest.Builder deserialize(CaptureRequest.Builder mdDefault, 627 JSONObject jsonReq) throws ItsException { 628 try { 629 Logt.i(TAG, "Parsing JSON capture request ..."); 630 631 // Iterate over the CaptureRequest reflected fields. 632 CaptureRequest.Builder md = mdDefault; 633 Field[] allFields = CaptureRequest.class.getDeclaredFields(); 634 for (Field field : allFields) { 635 if (Modifier.isPublic(field.getModifiers()) && 636 Modifier.isStatic(field.getModifiers()) && 637 field.getType() == CaptureRequest.Key.class && 638 field.getGenericType() instanceof ParameterizedType) { 639 ParameterizedType paramType = (ParameterizedType)field.getGenericType(); 640 Type[] argTypes = paramType.getActualTypeArguments(); 641 if (argTypes.length > 0) { 642 CaptureRequest.Key key = (CaptureRequest.Key)field.get(md); 643 String keyName = key.getName(); 644 Type keyType = argTypes[0]; 645 646 // For each reflected CaptureRequest entry, look inside the JSON object 647 // to see if it is being set. If it is found, remove the key from the 648 // JSON object. After this process, there should be no keys left in the 649 // JSON (otherwise an invalid key was specified). 650 651 if (jsonReq.has(keyName) && !jsonReq.isNull(keyName)) { 652 if (keyType instanceof GenericArrayType) { 653 Type elmtType = 654 ((GenericArrayType)keyType).getGenericComponentType(); 655 JSONArray ja = jsonReq.getJSONArray(keyName); 656 Object val[] = new Object[ja.length()]; 657 for (int i = 0; i < ja.length(); i++) { 658 if (elmtType == int.class) { 659 Array.set(val, i, ja.getInt(i)); 660 } else if (elmtType == byte.class) { 661 Array.set(val, i, (byte)ja.getInt(i)); 662 } else if (elmtType == float.class) { 663 Array.set(val, i, (float)ja.getDouble(i)); 664 } else if (elmtType == long.class) { 665 Array.set(val, i, ja.getLong(i)); 666 } else if (elmtType == double.class) { 667 Array.set(val, i, ja.getDouble(i)); 668 } else if (elmtType == boolean.class) { 669 Array.set(val, i, ja.getBoolean(i)); 670 } else if (elmtType == String.class) { 671 Array.set(val, i, ja.getString(i)); 672 } else if (elmtType == Size.class){ 673 JSONObject obj = ja.getJSONObject(i); 674 Array.set(val, i, new Size( 675 obj.getInt("width"), obj.getInt("height"))); 676 } else if (elmtType == Rect.class) { 677 JSONObject obj = ja.getJSONObject(i); 678 Array.set(val, i, new Rect( 679 obj.getInt("left"), obj.getInt("top"), 680 obj.getInt("bottom"), obj.getInt("right"))); 681 } else if (elmtType == Rational.class) { 682 JSONObject obj = ja.getJSONObject(i); 683 Array.set(val, i, new Rational( 684 obj.getInt("numerator"), 685 obj.getInt("denominator"))); 686 } else if (elmtType == RggbChannelVector.class) { 687 JSONArray arr = ja.getJSONArray(i); 688 Array.set(val, i, new RggbChannelVector( 689 (float)arr.getDouble(0), 690 (float)arr.getDouble(1), 691 (float)arr.getDouble(2), 692 (float)arr.getDouble(3))); 693 } else if (elmtType == ColorSpaceTransform.class) { 694 JSONArray arr = ja.getJSONArray(i); 695 Rational xform[] = new Rational[9]; 696 for (int j = 0; j < 9; j++) { 697 xform[j] = new Rational( 698 arr.getJSONObject(j).getInt("numerator"), 699 arr.getJSONObject(j).getInt("denominator")); 700 } 701 Array.set(val, i, new ColorSpaceTransform(xform)); 702 } else if (elmtType == MeteringRectangle.class) { 703 JSONObject obj = ja.getJSONObject(i); 704 Array.set(val, i, new MeteringRectangle( 705 obj.getInt("x"), 706 obj.getInt("y"), 707 obj.getInt("width"), 708 obj.getInt("height"), 709 obj.getInt("weight"))); 710 } else { 711 throw new ItsException( 712 "Failed to parse key from JSON: " + keyName); 713 } 714 } 715 if (val != null) { 716 Logt.i(TAG, "Set: "+keyName+" -> "+Arrays.toString(val)); 717 md.set(key, val); 718 jsonReq.remove(keyName); 719 } 720 } else { 721 Object val = null; 722 if (keyType == Integer.class) { 723 val = jsonReq.getInt(keyName); 724 } else if (keyType == Byte.class) { 725 val = (byte)jsonReq.getInt(keyName); 726 } else if (keyType == Double.class) { 727 val = jsonReq.getDouble(keyName); 728 } else if (keyType == Long.class) { 729 val = jsonReq.getLong(keyName); 730 } else if (keyType == Float.class) { 731 val = (float)jsonReq.getDouble(keyName); 732 } else if (keyType == Boolean.class) { 733 val = jsonReq.getBoolean(keyName); 734 } else if (keyType == String.class) { 735 val = jsonReq.getString(keyName); 736 } else if (keyType == Size.class) { 737 JSONObject obj = jsonReq.getJSONObject(keyName); 738 val = new Size( 739 obj.getInt("width"), obj.getInt("height")); 740 } else if (keyType == Rect.class) { 741 JSONObject obj = jsonReq.getJSONObject(keyName); 742 val = new Rect( 743 obj.getInt("left"), obj.getInt("top"), 744 obj.getInt("right"), obj.getInt("bottom")); 745 } else if (keyType == Rational.class) { 746 JSONObject obj = jsonReq.getJSONObject(keyName); 747 val = new Rational(obj.getInt("numerator"), 748 obj.getInt("denominator")); 749 } else if (keyType == RggbChannelVector.class) { 750 JSONObject obj = jsonReq.optJSONObject(keyName); 751 JSONArray arr = jsonReq.optJSONArray(keyName); 752 if (arr != null) { 753 val = new RggbChannelVector( 754 (float)arr.getDouble(0), 755 (float)arr.getDouble(1), 756 (float)arr.getDouble(2), 757 (float)arr.getDouble(3)); 758 } else if (obj != null) { 759 val = new RggbChannelVector( 760 (float)obj.getDouble("red"), 761 (float)obj.getDouble("greenEven"), 762 (float)obj.getDouble("greenOdd"), 763 (float)obj.getDouble("blue")); 764 } else { 765 throw new ItsException("Invalid RggbChannelVector object"); 766 } 767 } else if (keyType == ColorSpaceTransform.class) { 768 JSONArray arr = jsonReq.getJSONArray(keyName); 769 Rational a[] = new Rational[9]; 770 for (int i = 0; i < 9; i++) { 771 a[i] = new Rational( 772 arr.getJSONObject(i).getInt("numerator"), 773 arr.getJSONObject(i).getInt("denominator")); 774 } 775 val = new ColorSpaceTransform(a); 776 } else if (keyType == TonemapCurve.class) { 777 JSONObject obj = jsonReq.optJSONObject(keyName); 778 String names[] = {"red", "green", "blue"}; 779 float[][] curves = new float[3][]; 780 for (int ch = 0; ch < 3; ch++) { 781 JSONArray ja = obj.getJSONArray(names[ch]); 782 curves[ch] = new float[ja.length()]; 783 for (int i = 0; i < ja.length(); i++) { 784 Array.set(curves[ch], i, (float)ja.getDouble(i)); 785 } 786 } 787 val = new TonemapCurve(curves[0], curves[1], curves[2]); 788 } else if (keyType instanceof ParameterizedType && 789 ((ParameterizedType)keyType).getRawType() == Range.class && 790 ((ParameterizedType)keyType).getActualTypeArguments().length == 1 && 791 ((ParameterizedType)keyType).getActualTypeArguments()[0] == Integer.class) { 792 JSONArray arr = jsonReq.getJSONArray(keyName); 793 val = new Range<Integer>(arr.getInt(0), arr.getInt(1)); 794 } else { 795 throw new ItsException( 796 "Failed to parse key from JSON: " + 797 keyName + ", " + keyType); 798 } 799 if (val != null) { 800 Logt.i(TAG, "Set: " + keyName + " -> " + val); 801 md.set(key ,val); 802 jsonReq.remove(keyName); 803 } 804 } 805 } 806 } 807 } 808 } 809 810 // Ensure that there were no invalid keys in the JSON request object. 811 if (jsonReq.length() != 0) { 812 throw new ItsException("Invalid JSON key(s): " + jsonReq.toString()); 813 } 814 815 Logt.i(TAG, "Parsing JSON capture request completed"); 816 return md; 817 } catch (java.lang.IllegalAccessException e) { 818 throw new ItsException("Access error: ", e); 819 } catch (org.json.JSONException e) { 820 throw new ItsException("JSON error: ", e); 821 } 822 } 823 824 @SuppressWarnings("unchecked") deserializeRequestList( CameraDevice device, JSONObject jsonObjTop, String requestKey)825 public static List<CaptureRequest.Builder> deserializeRequestList( 826 CameraDevice device, JSONObject jsonObjTop, String requestKey) 827 throws ItsException { 828 try { 829 List<CaptureRequest.Builder> requests = null; 830 JSONArray jsonReqs = jsonObjTop.getJSONArray(requestKey); 831 requests = new LinkedList<CaptureRequest.Builder>(); 832 for (int i = 0; i < jsonReqs.length(); i++) { 833 CaptureRequest.Builder templateReq = device.createCaptureRequest( 834 CameraDevice.TEMPLATE_STILL_CAPTURE); 835 requests.add( 836 deserialize(templateReq, jsonReqs.getJSONObject(i))); 837 } 838 return requests; 839 } catch (org.json.JSONException e) { 840 throw new ItsException("JSON error: ", e); 841 } catch (android.hardware.camera2.CameraAccessException e) { 842 throw new ItsException("Access error: ", e); 843 } 844 } 845 } 846