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