1 /* 2 * Copyright (C) 2014 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 android.hardware.camera2.cts.helpers; 18 19 import static com.android.ex.camera2.blocking.BlockingStateCallback.*; 20 21 import android.graphics.Point; 22 import android.graphics.Rect; 23 import android.hardware.camera2.CameraAccessException; 24 import android.hardware.camera2.CameraCharacteristics; 25 import android.hardware.camera2.CameraDevice; 26 import android.hardware.camera2.CameraManager; 27 import android.hardware.camera2.CameraMetadata; 28 import android.hardware.camera2.CaptureResult; 29 import android.hardware.camera2.CaptureRequest; 30 import android.hardware.camera2.TotalCaptureResult; 31 import android.hardware.camera2.params.BlackLevelPattern; 32 import android.hardware.camera2.params.ColorSpaceTransform; 33 import android.hardware.camera2.params.Face; 34 import android.hardware.camera2.params.LensShadingMap; 35 import android.hardware.camera2.params.MeteringRectangle; 36 import android.hardware.camera2.params.RggbChannelVector; 37 import android.hardware.camera2.params.StreamConfigurationMap; 38 import android.hardware.camera2.params.TonemapCurve; 39 import android.location.Location; 40 import android.os.Handler; 41 import android.os.HandlerThread; 42 import android.util.Log; 43 import android.util.Pair; 44 import android.util.Rational; 45 import android.util.Size; 46 import android.util.SizeF; 47 import android.util.Range; 48 49 import com.android.ex.camera2.blocking.BlockingCameraManager; 50 import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException; 51 import com.android.ex.camera2.blocking.BlockingStateCallback; 52 53 import org.json.JSONArray; 54 import org.json.JSONObject; 55 56 import java.lang.reflect.Array; 57 import java.lang.reflect.Field; 58 import java.lang.reflect.GenericArrayType; 59 import java.lang.reflect.Modifier; 60 import java.lang.reflect.ParameterizedType; 61 import java.lang.reflect.Type; 62 63 /** 64 * Utility class to dump the camera metadata. 65 */ 66 public final class CameraMetadataGetter implements AutoCloseable { 67 private static final String TAG = CameraMetadataGetter.class.getSimpleName(); 68 private static final int CAMERA_CLOSE_TIMEOUT_MS = 5000; 69 public static final int[] TEMPLATE_IDS = { 70 CameraDevice.TEMPLATE_PREVIEW, 71 CameraDevice.TEMPLATE_STILL_CAPTURE, 72 CameraDevice.TEMPLATE_RECORD, 73 CameraDevice.TEMPLATE_VIDEO_SNAPSHOT, 74 CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG, 75 CameraDevice.TEMPLATE_MANUAL, 76 }; 77 private CameraManager mCameraManager; 78 private BlockingStateCallback mCameraListener; 79 private HandlerThread mHandlerThread; 80 private Handler mHandler; 81 82 private static class MetadataEntry { 83 public MetadataEntry(String k, Object v) { 84 key = k; 85 value = v; 86 } 87 88 public String key; 89 public Object value; 90 } 91 92 public CameraMetadataGetter(CameraManager cameraManager) { 93 if (cameraManager == null) { 94 throw new IllegalArgumentException("can not create an CameraMetadataGetter object" 95 + " with null CameraManager"); 96 } 97 98 mCameraManager = cameraManager; 99 100 mCameraListener = new BlockingStateCallback(); 101 mHandlerThread = new HandlerThread(TAG); 102 mHandlerThread.start(); 103 mHandler = new Handler(mHandlerThread.getLooper()); 104 } 105 106 public String getCameraInfo() { 107 StringBuffer cameraInfo = new StringBuffer("{\"CameraStaticMetadata\":{"); 108 CameraCharacteristics staticMetadata; 109 String[] cameraIds; 110 try { 111 cameraIds = mCameraManager.getCameraIdList(); 112 } catch (CameraAccessException e) { 113 Log.e(TAG, "Unable to get camera ids, skip this info, error: " + e.getMessage()); 114 return ""; 115 } 116 for (String id : cameraIds) { 117 String value = null; 118 try { 119 staticMetadata = mCameraManager.getCameraCharacteristics(id); 120 value = serialize(staticMetadata).toString(); 121 } catch (CameraAccessException e) { 122 Log.e(TAG, 123 "Unable to get camera camera static info, skip this camera, error: " 124 + e.getMessage()); 125 } 126 cameraInfo.append("\"camera" + id + "\":"); // Key 127 cameraInfo.append(value); // Value 128 // If not last, print "," // Separator 129 if (!id.equals(cameraIds[cameraIds.length - 1])) { 130 cameraInfo.append(","); 131 } 132 } 133 cameraInfo.append("}}"); 134 135 return cameraInfo.toString(); 136 } 137 138 public JSONObject getCameraInfo(String cameraId) { 139 JSONObject staticMetadata = null; 140 try { 141 staticMetadata = serialize(mCameraManager.getCameraCharacteristics(cameraId)); 142 } catch (CameraAccessException e) { 143 Log.e(TAG, 144 "Unable to get camera camera static info, skip this camera, error: " 145 + e.getMessage()); 146 } 147 return staticMetadata; 148 } 149 150 public JSONObject[] getCaptureRequestTemplates(String cameraId) { 151 JSONObject[] templates = new JSONObject[TEMPLATE_IDS.length]; 152 CameraDevice camera = null; 153 try { 154 camera = (new BlockingCameraManager(mCameraManager)).openCamera(cameraId, 155 mCameraListener, mHandler); 156 for (int i = 0; i < TEMPLATE_IDS.length; i++) { 157 CaptureRequest.Builder request; 158 try { 159 request = camera.createCaptureRequest(TEMPLATE_IDS[i]); 160 templates[i] = serialize(request.build()); 161 } catch (Exception e) { 162 Log.e(TAG, "Unable to create template " + TEMPLATE_IDS[i] 163 + " because of error " + e.getMessage()); 164 templates[i] = null; 165 } 166 } 167 return templates; 168 } catch (CameraAccessException | BlockingOpenException e) { 169 Log.e(TAG, "Unable to open camera " + cameraId + " because of error " 170 + e.getMessage()); 171 return new JSONObject[0]; 172 } finally { 173 if (camera != null) { 174 camera.close(); 175 } 176 } 177 } 178 179 public String getCaptureRequestTemplates() { 180 StringBuffer templates = new StringBuffer("{\"CameraRequestTemplates\":{"); 181 String[] cameraIds; 182 try { 183 cameraIds = mCameraManager.getCameraIdList(); 184 } catch (CameraAccessException e) { 185 Log.e(TAG, "Unable to get camera ids, skip this info, error: " + e.getMessage()); 186 return ""; 187 } 188 CameraDevice camera = null; 189 for (String id : cameraIds) { 190 try { 191 try { 192 camera = (new BlockingCameraManager(mCameraManager)).openCamera(id, 193 mCameraListener, mHandler); 194 } catch (CameraAccessException | BlockingOpenException e) { 195 Log.e(TAG, "Unable to open camera " + id + " because of error " 196 + e.getMessage()); 197 continue; 198 } 199 200 for (int i = 0; i < TEMPLATE_IDS.length; i++) { 201 String value = null; 202 CaptureRequest.Builder request; 203 try { 204 request = camera.createCaptureRequest(TEMPLATE_IDS[i]); 205 value = serialize(request.build()).toString(); 206 } catch (Exception e) { 207 Log.e(TAG, "Unable to create template " + TEMPLATE_IDS[i] 208 + " because of error " + e.getMessage()); 209 } 210 templates.append("\"Camera" + id + "CaptureTemplate" + 211 TEMPLATE_IDS[i] + "\":"); 212 templates.append(value); 213 if (!id.equals(cameraIds[cameraIds.length - 1]) || 214 i < (TEMPLATE_IDS.length - 1)) { 215 templates.append(","); 216 } 217 } 218 } finally { 219 if (camera != null) { 220 camera.close(); 221 mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS); 222 } 223 } 224 } 225 226 templates.append("}}"); 227 return templates.toString(); 228 } 229 230 /* 231 * Cleanup the resources. 232 */ 233 @Override 234 public void close() throws Exception { 235 mHandlerThread.quitSafely(); 236 } 237 238 @Override 239 protected void finalize() throws Throwable { 240 try { 241 close(); 242 } finally { 243 super.finalize(); 244 } 245 } 246 247 @SuppressWarnings("unchecked") 248 private static Object serializeRational(Rational rat) throws org.json.JSONException { 249 JSONObject ratObj = new JSONObject(); 250 ratObj.put("numerator", rat.getNumerator()); 251 ratObj.put("denominator", rat.getDenominator()); 252 return ratObj; 253 } 254 255 @SuppressWarnings("unchecked") 256 private static Object serializeSize(Size size) throws org.json.JSONException { 257 JSONObject sizeObj = new JSONObject(); 258 sizeObj.put("width", size.getWidth()); 259 sizeObj.put("height", size.getHeight()); 260 return sizeObj; 261 } 262 263 @SuppressWarnings("unchecked") 264 private static Object serializeSizeF(SizeF size) throws org.json.JSONException { 265 JSONObject sizeObj = new JSONObject(); 266 sizeObj.put("width", size.getWidth()); 267 sizeObj.put("height", size.getHeight()); 268 return sizeObj; 269 } 270 271 @SuppressWarnings("unchecked") 272 private static Object serializeRect(Rect rect) throws org.json.JSONException { 273 JSONObject rectObj = new JSONObject(); 274 rectObj.put("left", rect.left); 275 rectObj.put("right", rect.right); 276 rectObj.put("top", rect.top); 277 rectObj.put("bottom", rect.bottom); 278 return rectObj; 279 } 280 281 private static Object serializePoint(Point point) throws org.json.JSONException { 282 JSONObject pointObj = new JSONObject(); 283 pointObj.put("x", point.x); 284 pointObj.put("y", point.y); 285 return pointObj; 286 } 287 288 @SuppressWarnings("unchecked") 289 private static Object serializeFace(Face face) 290 throws org.json.JSONException { 291 JSONObject faceObj = new JSONObject(); 292 faceObj.put("bounds", serializeRect(face.getBounds())); 293 faceObj.put("score", face.getScore()); 294 faceObj.put("id", face.getId()); 295 faceObj.put("leftEye", serializePoint(face.getLeftEyePosition())); 296 faceObj.put("rightEye", serializePoint(face.getRightEyePosition())); 297 faceObj.put("mouth", serializePoint(face.getMouthPosition())); 298 return faceObj; 299 } 300 301 @SuppressWarnings("unchecked") 302 private static Object serializeStreamConfigurationMap( 303 StreamConfigurationMap map) 304 throws org.json.JSONException { 305 // TODO: Serialize the rest of the StreamConfigurationMap fields. 306 JSONObject mapObj = new JSONObject(); 307 JSONArray cfgArray = new JSONArray(); 308 int fmts[] = map.getOutputFormats(); 309 if (fmts != null) { 310 for (int fi = 0; fi < Array.getLength(fmts); fi++) { 311 Size sizes[] = map.getOutputSizes(fmts[fi]); 312 if (sizes != null) { 313 for (int si = 0; si < Array.getLength(sizes); si++) { 314 JSONObject obj = new JSONObject(); 315 obj.put("format", fmts[fi]); 316 obj.put("width", sizes[si].getWidth()); 317 obj.put("height", sizes[si].getHeight()); 318 obj.put("input", false); 319 obj.put("minFrameDuration", 320 map.getOutputMinFrameDuration(fmts[fi], sizes[si])); 321 cfgArray.put(obj); 322 } 323 } 324 } 325 } 326 mapObj.put("availableStreamConfigurations", cfgArray); 327 return mapObj; 328 } 329 330 @SuppressWarnings("unchecked") 331 private static Object serializeMeteringRectangle(MeteringRectangle rect) 332 throws org.json.JSONException { 333 JSONObject rectObj = new JSONObject(); 334 rectObj.put("x", rect.getX()); 335 rectObj.put("y", rect.getY()); 336 rectObj.put("width", rect.getWidth()); 337 rectObj.put("height", rect.getHeight()); 338 rectObj.put("weight", rect.getMeteringWeight()); 339 return rectObj; 340 } 341 342 @SuppressWarnings("unchecked") 343 private static Object serializePair(Pair pair) 344 throws org.json.JSONException { 345 JSONArray pairObj = new JSONArray(); 346 pairObj.put(pair.first); 347 pairObj.put(pair.second); 348 return pairObj; 349 } 350 351 @SuppressWarnings("unchecked") 352 private static Object serializeRange(Range range) 353 throws org.json.JSONException { 354 JSONArray rangeObj = new JSONArray(); 355 rangeObj.put(range.getLower()); 356 rangeObj.put(range.getUpper()); 357 return rangeObj; 358 } 359 360 @SuppressWarnings("unchecked") 361 private static Object serializeColorSpaceTransform(ColorSpaceTransform xform) 362 throws org.json.JSONException { 363 JSONArray xformObj = new JSONArray(); 364 for (int row = 0; row < 3; row++) { 365 for (int col = 0; col < 3; col++) { 366 xformObj.put(serializeRational(xform.getElement(col, row))); 367 } 368 } 369 return xformObj; 370 } 371 372 @SuppressWarnings("unchecked") 373 private static Object serializeTonemapCurve(TonemapCurve curve) 374 throws org.json.JSONException { 375 JSONObject curveObj = new JSONObject(); 376 String names[] = { 377 "red", "green", "blue" }; 378 for (int ch = 0; ch < 3; ch++) { 379 JSONArray curveArr = new JSONArray(); 380 int len = curve.getPointCount(ch); 381 for (int i = 0; i < len; i++) { 382 curveArr.put(curve.getPoint(ch, i).x); 383 curveArr.put(curve.getPoint(ch, i).y); 384 } 385 curveObj.put(names[ch], curveArr); 386 } 387 return curveObj; 388 } 389 390 @SuppressWarnings("unchecked") 391 private static Object serializeRggbChannelVector(RggbChannelVector vec) 392 throws org.json.JSONException { 393 JSONArray vecObj = new JSONArray(); 394 vecObj.put(vec.getRed()); 395 vecObj.put(vec.getGreenEven()); 396 vecObj.put(vec.getGreenOdd()); 397 vecObj.put(vec.getBlue()); 398 return vecObj; 399 } 400 401 @SuppressWarnings("unchecked") 402 private static Object serializeBlackLevelPattern(BlackLevelPattern pat) 403 throws org.json.JSONException { 404 int patVals[] = new int[4]; 405 pat.copyTo(patVals, 0); 406 JSONArray patObj = new JSONArray(); 407 patObj.put(patVals[0]); 408 patObj.put(patVals[1]); 409 patObj.put(patVals[2]); 410 patObj.put(patVals[3]); 411 return patObj; 412 } 413 414 @SuppressWarnings("unchecked") 415 private static Object serializeLocation(Location loc) 416 throws org.json.JSONException { 417 return loc.toString(); 418 } 419 420 @SuppressWarnings("unchecked") 421 private static Object serializeLensShadingMap(LensShadingMap map) 422 throws org.json.JSONException { 423 JSONArray mapObj = new JSONArray(); 424 for (int row = 0; row < map.getRowCount(); row++) { 425 for (int col = 0; col < map.getColumnCount(); col++) { 426 for (int ch = 0; ch < 4; ch++) { 427 mapObj.put(map.getGainFactor(ch, col, row)); 428 } 429 } 430 } 431 return mapObj; 432 } 433 434 private static String getKeyName(Object keyObj) { 435 if (keyObj.getClass() == CaptureResult.Key.class 436 || keyObj.getClass() == TotalCaptureResult.class) { 437 return ((CaptureResult.Key) keyObj).getName(); 438 } else if (keyObj.getClass() == CaptureRequest.Key.class) { 439 return ((CaptureRequest.Key) keyObj).getName(); 440 } else if (keyObj.getClass() == CameraCharacteristics.Key.class) { 441 return ((CameraCharacteristics.Key) keyObj).getName(); 442 } 443 444 throw new IllegalArgumentException("Invalid key object"); 445 } 446 447 private static Object getKeyValue(CameraMetadata md, Object keyObj) { 448 if (md.getClass() == CaptureResult.class || md.getClass() == TotalCaptureResult.class) { 449 return ((CaptureResult) md).get((CaptureResult.Key) keyObj); 450 } else if (md.getClass() == CaptureRequest.class) { 451 return ((CaptureRequest) md).get((CaptureRequest.Key) keyObj); 452 } else if (md.getClass() == CameraCharacteristics.class) { 453 return ((CameraCharacteristics) md).get((CameraCharacteristics.Key) keyObj); 454 } 455 456 throw new IllegalArgumentException("Invalid key object"); 457 } 458 459 @SuppressWarnings("unchecked") 460 private static MetadataEntry serializeEntry(Type keyType, Object keyObj, CameraMetadata md) { 461 String keyName = getKeyName(keyObj); 462 463 try { 464 Object keyValue = getKeyValue(md, keyObj); 465 if (keyValue == null) { 466 return new MetadataEntry(keyName, JSONObject.NULL); 467 } else if (keyType == Float.class) { 468 // The JSON serializer doesn't handle floating point NaN or Inf. 469 if (((Float) keyValue).isInfinite() || ((Float) keyValue).isNaN()) { 470 Log.w(TAG, "Inf/NaN floating point value serialized: " + keyName); 471 return null; 472 } 473 return new MetadataEntry(keyName, keyValue); 474 } else if (keyType == Integer.class || keyType == Long.class || keyType == Byte.class || 475 keyType == Boolean.class || keyType == String.class) { 476 return new MetadataEntry(keyName, keyValue); 477 } else if (keyType == Rational.class) { 478 return new MetadataEntry(keyName, serializeRational((Rational) keyValue)); 479 } else if (keyType == Size.class) { 480 return new MetadataEntry(keyName, serializeSize((Size) keyValue)); 481 } else if (keyType == SizeF.class) { 482 return new MetadataEntry(keyName, serializeSizeF((SizeF) keyValue)); 483 } else if (keyType == Rect.class) { 484 return new MetadataEntry(keyName, serializeRect((Rect) keyValue)); 485 } else if (keyType == Face.class) { 486 return new MetadataEntry(keyName, serializeFace((Face) keyValue)); 487 } else if (keyType == StreamConfigurationMap.class) { 488 return new MetadataEntry(keyName, 489 serializeStreamConfigurationMap((StreamConfigurationMap) keyValue)); 490 } else if (keyType instanceof ParameterizedType && 491 ((ParameterizedType) keyType).getRawType() == Range.class) { 492 return new MetadataEntry(keyName, serializeRange((Range) keyValue)); 493 } else if (keyType == ColorSpaceTransform.class) { 494 return new MetadataEntry(keyName, 495 serializeColorSpaceTransform((ColorSpaceTransform) keyValue)); 496 } else if (keyType == MeteringRectangle.class) { 497 return new MetadataEntry(keyName, 498 serializeMeteringRectangle((MeteringRectangle) keyValue)); 499 } else if (keyType == Location.class) { 500 return new MetadataEntry(keyName, 501 serializeLocation((Location) keyValue)); 502 } else if (keyType == RggbChannelVector.class) { 503 return new MetadataEntry(keyName, 504 serializeRggbChannelVector((RggbChannelVector) keyValue)); 505 } else if (keyType == BlackLevelPattern.class) { 506 return new MetadataEntry(keyName, 507 serializeBlackLevelPattern((BlackLevelPattern) keyValue)); 508 } else if (keyType == TonemapCurve.class) { 509 return new MetadataEntry(keyName, 510 serializeTonemapCurve((TonemapCurve) keyValue)); 511 } else if (keyType == Point.class) { 512 return new MetadataEntry(keyName, 513 serializePoint((Point) keyValue)); 514 } else if (keyType == LensShadingMap.class) { 515 return new MetadataEntry(keyName, 516 serializeLensShadingMap((LensShadingMap) keyValue)); 517 } else { 518 Log.w(TAG, String.format("Serializing unsupported key type: " + keyType)); 519 return null; 520 } 521 } catch (org.json.JSONException e) { 522 throw new IllegalStateException("JSON error for key: " + keyName + ": ", e); 523 } 524 } 525 526 @SuppressWarnings("unchecked") 527 private static MetadataEntry serializeArrayEntry(Type keyType, Object keyObj, 528 CameraMetadata md) { 529 String keyName = getKeyName(keyObj); 530 try { 531 Object keyValue = getKeyValue(md, keyObj); 532 if (keyValue == null) { 533 return new MetadataEntry(keyName, JSONObject.NULL); 534 } 535 int arrayLen = Array.getLength(keyValue); 536 Type elmtType = ((GenericArrayType) keyType).getGenericComponentType(); 537 if (elmtType == int.class || elmtType == float.class || elmtType == byte.class || 538 elmtType == long.class || elmtType == double.class 539 || elmtType == boolean.class) { 540 return new MetadataEntry(keyName, new JSONArray(keyValue)); 541 } else if (elmtType == Rational.class) { 542 JSONArray jsonArray = new JSONArray(); 543 for (int i = 0; i < arrayLen; i++) { 544 jsonArray.put(serializeRational((Rational) Array.get(keyValue, i))); 545 } 546 return new MetadataEntry(keyName, jsonArray); 547 } else if (elmtType == Size.class) { 548 JSONArray jsonArray = new JSONArray(); 549 for (int i = 0; i < arrayLen; i++) { 550 jsonArray.put(serializeSize((Size) Array.get(keyValue, i))); 551 } 552 return new MetadataEntry(keyName, jsonArray); 553 } else if (elmtType == Rect.class) { 554 JSONArray jsonArray = new JSONArray(); 555 for (int i = 0; i < arrayLen; i++) { 556 jsonArray.put(serializeRect((Rect) Array.get(keyValue, i))); 557 } 558 return new MetadataEntry(keyName, jsonArray); 559 } else if (elmtType == Face.class) { 560 JSONArray jsonArray = new JSONArray(); 561 for (int i = 0; i < arrayLen; i++) { 562 jsonArray.put(serializeFace((Face) Array.get(keyValue, i))); 563 } 564 return new MetadataEntry(keyName, jsonArray); 565 } else if (elmtType == StreamConfigurationMap.class) { 566 JSONArray jsonArray = new JSONArray(); 567 for (int i = 0; i < arrayLen; i++) { 568 jsonArray.put(serializeStreamConfigurationMap( 569 (StreamConfigurationMap) Array.get(keyValue, i))); 570 } 571 return new MetadataEntry(keyName, jsonArray); 572 } else if (elmtType instanceof ParameterizedType && 573 ((ParameterizedType) elmtType).getRawType() == Range.class) { 574 JSONArray jsonArray = new JSONArray(); 575 for (int i = 0; i < arrayLen; i++) { 576 jsonArray.put(serializeRange((Range) Array.get(keyValue, i))); 577 } 578 return new MetadataEntry(keyName, jsonArray); 579 } else if (elmtType instanceof ParameterizedType && 580 ((ParameterizedType) elmtType).getRawType() == Pair.class) { 581 JSONArray jsonArray = new JSONArray(); 582 for (int i = 0; i < arrayLen; i++) { 583 jsonArray.put(serializePair((Pair) Array.get(keyValue, i))); 584 } 585 return new MetadataEntry(keyName, jsonArray); 586 } else if (elmtType == MeteringRectangle.class) { 587 JSONArray jsonArray = new JSONArray(); 588 for (int i = 0; i < arrayLen; i++) { 589 jsonArray.put(serializeMeteringRectangle( 590 (MeteringRectangle) Array.get(keyValue, i))); 591 } 592 return new MetadataEntry(keyName, jsonArray); 593 } else if (elmtType == Location.class) { 594 JSONArray jsonArray = new JSONArray(); 595 for (int i = 0; i < arrayLen; i++) { 596 jsonArray.put(serializeLocation((Location) Array.get(keyValue, i))); 597 } 598 return new MetadataEntry(keyName, jsonArray); 599 } else if (elmtType == RggbChannelVector.class) { 600 JSONArray jsonArray = new JSONArray(); 601 for (int i = 0; i < arrayLen; i++) { 602 jsonArray.put(serializeRggbChannelVector( 603 (RggbChannelVector) Array.get(keyValue, i))); 604 } 605 return new MetadataEntry(keyName, jsonArray); 606 } else if (elmtType == BlackLevelPattern.class) { 607 JSONArray jsonArray = new JSONArray(); 608 for (int i = 0; i < arrayLen; i++) { 609 jsonArray.put(serializeBlackLevelPattern( 610 (BlackLevelPattern) Array.get(keyValue, i))); 611 } 612 return new MetadataEntry(keyName, jsonArray); 613 } else if (elmtType == Point.class) { 614 JSONArray jsonArray = new JSONArray(); 615 for (int i = 0; i < arrayLen; i++) { 616 jsonArray.put(serializePoint((Point) Array.get(keyValue, i))); 617 } 618 return new MetadataEntry(keyName, jsonArray); 619 } else { 620 Log.w(TAG, String.format("Serializing unsupported array type: " + elmtType)); 621 return null; 622 } 623 } catch (org.json.JSONException e) { 624 throw new IllegalStateException("JSON error for key: " + keyName + ": ", e); 625 } 626 } 627 628 @SuppressWarnings("unchecked") 629 private static JSONObject serialize(CameraMetadata md) { 630 JSONObject jsonObj = new JSONObject(); 631 Field[] allFields = md.getClass().getDeclaredFields(); 632 if (md.getClass() == TotalCaptureResult.class) { 633 allFields = CaptureResult.class.getDeclaredFields(); 634 } 635 for (Field field : allFields) { 636 if (Modifier.isPublic(field.getModifiers()) && 637 Modifier.isStatic(field.getModifiers()) && 638 (field.getType() == CaptureRequest.Key.class 639 || field.getType() == CaptureResult.Key.class 640 || field.getType() == TotalCaptureResult.Key.class 641 || field.getType() == CameraCharacteristics.Key.class) 642 && 643 field.getGenericType() instanceof ParameterizedType) { 644 ParameterizedType paramType = (ParameterizedType) field.getGenericType(); 645 Type[] argTypes = paramType.getActualTypeArguments(); 646 if (argTypes.length > 0) { 647 try { 648 Type keyType = argTypes[0]; 649 Object keyObj = field.get(md); 650 MetadataEntry entry; 651 if (keyType instanceof GenericArrayType) { 652 entry = serializeArrayEntry(keyType, keyObj, md); 653 } else { 654 entry = serializeEntry(keyType, keyObj, md); 655 } 656 657 // TODO: Figure this weird case out. 658 // There is a weird case where the entry is non-null but 659 // the toString 660 // of the entry is null, and if this happens, the 661 // null-ness spreads like 662 // a virus and makes the whole JSON object null from the 663 // top level down. 664 // Not sure if it's a bug in the library or I'm just not 665 // using it right. 666 // Workaround by checking for this case explicitly and 667 // not adding the 668 // value to the jsonObj when it is detected. 669 if (entry != null && entry.key != null && entry.value != null 670 && entry.value.toString() == null) { 671 Log.w(TAG, "Error encountered serializing value for key: " 672 + entry.key); 673 } else if (entry != null) { 674 jsonObj.put(entry.key, entry.value); 675 } else { 676 // Ignore. 677 } 678 } catch (IllegalAccessException e) { 679 throw new IllegalStateException( 680 "Access error for field: " + field + ": ", e); 681 } catch (org.json.JSONException e) { 682 throw new IllegalStateException( 683 "JSON error for field: " + field + ": ", e); 684 } 685 } 686 } 687 } 688 return jsonObj; 689 } 690 } 691