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 17package android.hardware.camera2.impl; 18 19import android.graphics.ImageFormat; 20import android.graphics.Point; 21import android.graphics.Rect; 22import android.hardware.camera2.CameraCharacteristics; 23import android.hardware.camera2.CameraMetadata; 24import android.hardware.camera2.CaptureResult; 25import android.hardware.camera2.Face; 26import android.hardware.camera2.Rational; 27import android.os.Parcelable; 28import android.os.Parcel; 29import android.util.Log; 30 31import java.lang.reflect.Array; 32import java.nio.ByteBuffer; 33import java.nio.ByteOrder; 34import java.util.ArrayList; 35import java.util.HashMap; 36 37/** 38 * Implementation of camera metadata marshal/unmarshal across Binder to 39 * the camera service 40 */ 41public class CameraMetadataNative extends CameraMetadata implements Parcelable { 42 43 private static final String TAG = "CameraMetadataJV"; 44 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 45 // this should be in sync with HAL_PIXEL_FORMAT_BLOB defined in graphics.h 46 private static final int NATIVE_JPEG_FORMAT = 0x21; 47 48 public CameraMetadataNative() { 49 super(); 50 mMetadataPtr = nativeAllocate(); 51 if (mMetadataPtr == 0) { 52 throw new OutOfMemoryError("Failed to allocate native CameraMetadata"); 53 } 54 } 55 56 /** 57 * Copy constructor - clone metadata 58 */ 59 public CameraMetadataNative(CameraMetadataNative other) { 60 super(); 61 mMetadataPtr = nativeAllocateCopy(other); 62 if (mMetadataPtr == 0) { 63 throw new OutOfMemoryError("Failed to allocate native CameraMetadata"); 64 } 65 } 66 67 public static final Parcelable.Creator<CameraMetadataNative> CREATOR = 68 new Parcelable.Creator<CameraMetadataNative>() { 69 @Override 70 public CameraMetadataNative createFromParcel(Parcel in) { 71 CameraMetadataNative metadata = new CameraMetadataNative(); 72 metadata.readFromParcel(in); 73 return metadata; 74 } 75 76 @Override 77 public CameraMetadataNative[] newArray(int size) { 78 return new CameraMetadataNative[size]; 79 } 80 }; 81 82 @Override 83 public int describeContents() { 84 return 0; 85 } 86 87 @Override 88 public void writeToParcel(Parcel dest, int flags) { 89 nativeWriteToParcel(dest); 90 } 91 92 @SuppressWarnings("unchecked") 93 @Override 94 public <T> T get(Key<T> key) { 95 T value = getOverride(key); 96 if (value != null) { 97 return value; 98 } 99 100 return getBase(key); 101 } 102 103 public void readFromParcel(Parcel in) { 104 nativeReadFromParcel(in); 105 } 106 107 /** 108 * Set a camera metadata field to a value. The field definitions can be 109 * found in {@link CameraCharacteristics}, {@link CaptureResult}, and 110 * {@link CaptureRequest}. 111 * 112 * @param key The metadata field to write. 113 * @param value The value to set the field to, which must be of a matching 114 * type to the key. 115 */ 116 public <T> void set(Key<T> key, T value) { 117 if (setOverride(key, value)) { 118 return; 119 } 120 121 setBase(key, value); 122 } 123 124 // Keep up-to-date with camera_metadata.h 125 /** 126 * @hide 127 */ 128 public static final int TYPE_BYTE = 0; 129 /** 130 * @hide 131 */ 132 public static final int TYPE_INT32 = 1; 133 /** 134 * @hide 135 */ 136 public static final int TYPE_FLOAT = 2; 137 /** 138 * @hide 139 */ 140 public static final int TYPE_INT64 = 3; 141 /** 142 * @hide 143 */ 144 public static final int TYPE_DOUBLE = 4; 145 /** 146 * @hide 147 */ 148 public static final int TYPE_RATIONAL = 5; 149 /** 150 * @hide 151 */ 152 public static final int NUM_TYPES = 6; 153 154 private void close() { 155 // this sets mMetadataPtr to 0 156 nativeClose(); 157 mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final 158 } 159 160 private static int getTypeSize(int nativeType) { 161 switch(nativeType) { 162 case TYPE_BYTE: 163 return 1; 164 case TYPE_INT32: 165 case TYPE_FLOAT: 166 return 4; 167 case TYPE_INT64: 168 case TYPE_DOUBLE: 169 case TYPE_RATIONAL: 170 return 8; 171 } 172 173 throw new UnsupportedOperationException("Unknown type, can't get size " 174 + nativeType); 175 } 176 177 private static Class<?> getExpectedType(int nativeType) { 178 switch(nativeType) { 179 case TYPE_BYTE: 180 return Byte.TYPE; 181 case TYPE_INT32: 182 return Integer.TYPE; 183 case TYPE_FLOAT: 184 return Float.TYPE; 185 case TYPE_INT64: 186 return Long.TYPE; 187 case TYPE_DOUBLE: 188 return Double.TYPE; 189 case TYPE_RATIONAL: 190 return Rational.class; 191 } 192 193 throw new UnsupportedOperationException("Unknown type, can't map to Java type " 194 + nativeType); 195 } 196 197 @SuppressWarnings("unchecked") 198 private static <T> int packSingleNative(T value, ByteBuffer buffer, Class<T> type, 199 int nativeType, boolean sizeOnly) { 200 201 if (!sizeOnly) { 202 /** 203 * Rewrite types when the native type doesn't match the managed type 204 * - Boolean -> Byte 205 * - Integer -> Byte 206 */ 207 208 if (nativeType == TYPE_BYTE && type == Boolean.TYPE) { 209 // Since a boolean can't be cast to byte, and we don't want to use putBoolean 210 boolean asBool = (Boolean) value; 211 byte asByte = (byte) (asBool ? 1 : 0); 212 value = (T) (Byte) asByte; 213 } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) { 214 int asInt = (Integer) value; 215 byte asByte = (byte) asInt; 216 value = (T) (Byte) asByte; 217 } else if (type != getExpectedType(nativeType)) { 218 throw new UnsupportedOperationException("Tried to pack a type of " + type + 219 " but we expected the type to be " + getExpectedType(nativeType)); 220 } 221 222 if (nativeType == TYPE_BYTE) { 223 buffer.put((Byte) value); 224 } else if (nativeType == TYPE_INT32) { 225 buffer.putInt((Integer) value); 226 } else if (nativeType == TYPE_FLOAT) { 227 buffer.putFloat((Float) value); 228 } else if (nativeType == TYPE_INT64) { 229 buffer.putLong((Long) value); 230 } else if (nativeType == TYPE_DOUBLE) { 231 buffer.putDouble((Double) value); 232 } else if (nativeType == TYPE_RATIONAL) { 233 Rational r = (Rational) value; 234 buffer.putInt(r.getNumerator()); 235 buffer.putInt(r.getDenominator()); 236 } 237 238 } 239 240 return getTypeSize(nativeType); 241 } 242 243 @SuppressWarnings({"unchecked", "rawtypes"}) 244 private static <T> int packSingle(T value, ByteBuffer buffer, Class<T> type, int nativeType, 245 boolean sizeOnly) { 246 247 int size = 0; 248 249 if (type.isPrimitive() || type == Rational.class) { 250 size = packSingleNative(value, buffer, type, nativeType, sizeOnly); 251 } else if (type.isEnum()) { 252 size = packEnum((Enum)value, buffer, (Class<Enum>)type, nativeType, sizeOnly); 253 } else if (type.isArray()) { 254 size = packArray(value, buffer, type, nativeType, sizeOnly); 255 } else { 256 size = packClass(value, buffer, type, nativeType, sizeOnly); 257 } 258 259 return size; 260 } 261 262 private static <T extends Enum<T>> int packEnum(T value, ByteBuffer buffer, Class<T> type, 263 int nativeType, boolean sizeOnly) { 264 265 // TODO: add support for enums with their own values. 266 return packSingleNative(getEnumValue(value), buffer, Integer.TYPE, nativeType, sizeOnly); 267 } 268 269 @SuppressWarnings("unchecked") 270 private static <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType, 271 boolean sizeOnly) { 272 273 MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType); 274 if (marshaler == null) { 275 throw new IllegalArgumentException(String.format("Unknown Key type: %s", type)); 276 } 277 278 return marshaler.marshal(value, buffer, nativeType, sizeOnly); 279 } 280 281 private static <T> int packArray(T value, ByteBuffer buffer, Class<T> type, int nativeType, 282 boolean sizeOnly) { 283 284 int size = 0; 285 int arrayLength = Array.getLength(value); 286 287 @SuppressWarnings("unchecked") 288 Class<Object> componentType = (Class<Object>)type.getComponentType(); 289 290 for (int i = 0; i < arrayLength; ++i) { 291 size += packSingle(Array.get(value, i), buffer, componentType, nativeType, sizeOnly); 292 } 293 294 return size; 295 } 296 297 @SuppressWarnings("unchecked") 298 private static <T> T unpackSingleNative(ByteBuffer buffer, Class<T> type, int nativeType) { 299 300 T val; 301 302 if (nativeType == TYPE_BYTE) { 303 val = (T) (Byte) buffer.get(); 304 } else if (nativeType == TYPE_INT32) { 305 val = (T) (Integer) buffer.getInt(); 306 } else if (nativeType == TYPE_FLOAT) { 307 val = (T) (Float) buffer.getFloat(); 308 } else if (nativeType == TYPE_INT64) { 309 val = (T) (Long) buffer.getLong(); 310 } else if (nativeType == TYPE_DOUBLE) { 311 val = (T) (Double) buffer.getDouble(); 312 } else if (nativeType == TYPE_RATIONAL) { 313 val = (T) new Rational(buffer.getInt(), buffer.getInt()); 314 } else { 315 throw new UnsupportedOperationException("Unknown type, can't unpack a native type " 316 + nativeType); 317 } 318 319 /** 320 * Rewrite types when the native type doesn't match the managed type 321 * - Byte -> Boolean 322 * - Byte -> Integer 323 */ 324 325 if (nativeType == TYPE_BYTE && type == Boolean.TYPE) { 326 // Since a boolean can't be cast to byte, and we don't want to use getBoolean 327 byte asByte = (Byte) val; 328 boolean asBool = asByte != 0; 329 val = (T) (Boolean) asBool; 330 } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) { 331 byte asByte = (Byte) val; 332 int asInt = asByte; 333 val = (T) (Integer) asInt; 334 } else if (type != getExpectedType(nativeType)) { 335 throw new UnsupportedOperationException("Tried to unpack a type of " + type + 336 " but we expected the type to be " + getExpectedType(nativeType)); 337 } 338 339 return val; 340 } 341 342 @SuppressWarnings({"unchecked", "rawtypes"}) 343 private static <T> T unpackSingle(ByteBuffer buffer, Class<T> type, int nativeType) { 344 345 if (type.isPrimitive() || type == Rational.class) { 346 return unpackSingleNative(buffer, type, nativeType); 347 } 348 349 if (type.isEnum()) { 350 return (T) unpackEnum(buffer, (Class<Enum>)type, nativeType); 351 } 352 353 if (type.isArray()) { 354 return unpackArray(buffer, type, nativeType); 355 } 356 357 T instance = unpackClass(buffer, type, nativeType); 358 359 return instance; 360 } 361 362 private static <T extends Enum<T>> T unpackEnum(ByteBuffer buffer, Class<T> type, 363 int nativeType) { 364 int ordinal = unpackSingleNative(buffer, Integer.TYPE, nativeType); 365 return getEnumFromValue(type, ordinal); 366 } 367 368 private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) { 369 370 MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType); 371 if (marshaler == null) { 372 throw new IllegalArgumentException("Unknown class type: " + type); 373 } 374 375 return marshaler.unmarshal(buffer, nativeType); 376 } 377 378 @SuppressWarnings("unchecked") 379 private static <T> T unpackArray(ByteBuffer buffer, Class<T> type, int nativeType) { 380 381 Class<?> componentType = type.getComponentType(); 382 Object array; 383 384 int elementSize = getTypeSize(nativeType); 385 386 MetadataMarshalClass<?> marshaler = getMarshaler(componentType, nativeType); 387 if (marshaler != null) { 388 elementSize = marshaler.getNativeSize(nativeType); 389 } 390 391 if (elementSize != MetadataMarshalClass.NATIVE_SIZE_DYNAMIC) { 392 int remaining = buffer.remaining(); 393 int arraySize = remaining / elementSize; 394 395 if (VERBOSE) { 396 Log.v(TAG, 397 String.format( 398 "Attempting to unpack array (count = %d, element size = %d, bytes " + 399 "remaining = %d) for type %s", 400 arraySize, elementSize, remaining, type)); 401 } 402 403 array = Array.newInstance(componentType, arraySize); 404 for (int i = 0; i < arraySize; ++i) { 405 Object elem = unpackSingle(buffer, componentType, nativeType); 406 Array.set(array, i, elem); 407 } 408 } else { 409 // Dynamic size, use an array list. 410 ArrayList<Object> arrayList = new ArrayList<Object>(); 411 412 int primitiveSize = getTypeSize(nativeType); 413 while (buffer.remaining() >= primitiveSize) { 414 Object elem = unpackSingle(buffer, componentType, nativeType); 415 arrayList.add(elem); 416 } 417 418 array = arrayList.toArray((T[]) Array.newInstance(componentType, 0)); 419 } 420 421 if (buffer.remaining() != 0) { 422 Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking " 423 + type); 424 } 425 426 return (T) array; 427 } 428 429 private <T> T getBase(Key<T> key) { 430 int tag = key.getTag(); 431 byte[] values = readValues(tag); 432 if (values == null) { 433 return null; 434 } 435 436 int nativeType = getNativeType(tag); 437 438 ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); 439 return unpackSingle(buffer, key.getType(), nativeType); 440 } 441 442 // Need overwrite some metadata that has different definitions between native 443 // and managed sides. 444 @SuppressWarnings("unchecked") 445 private <T> T getOverride(Key<T> key) { 446 if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)) { 447 return (T) getAvailableFormats(); 448 } else if (key.equals(CaptureResult.STATISTICS_FACES)) { 449 return (T) getFaces(); 450 } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) { 451 return (T) fixFaceRectangles(); 452 } 453 454 // For other keys, get() falls back to getBase() 455 return null; 456 } 457 458 private int[] getAvailableFormats() { 459 int[] availableFormats = getBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS); 460 for (int i = 0; i < availableFormats.length; i++) { 461 // JPEG has different value between native and managed side, need override. 462 if (availableFormats[i] == NATIVE_JPEG_FORMAT) { 463 availableFormats[i] = ImageFormat.JPEG; 464 } 465 } 466 return availableFormats; 467 } 468 469 private Face[] getFaces() { 470 final int FACE_LANDMARK_SIZE = 6; 471 472 Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE); 473 if (faceDetectMode == null) { 474 Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE"); 475 faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE; 476 } else { 477 if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) { 478 return new Face[0]; 479 } 480 if (faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE && 481 faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) { 482 Log.w(TAG, "Unknown face detect mode: " + faceDetectMode); 483 return new Face[0]; 484 } 485 } 486 487 // Face scores and rectangles are required by SIMPLE and FULL mode. 488 byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES); 489 Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES); 490 if (faceScores == null || faceRectangles == null) { 491 Log.w(TAG, "Expect face scores and rectangles to be non-null"); 492 return new Face[0]; 493 } else if (faceScores.length != faceRectangles.length) { 494 Log.w(TAG, String.format("Face score size(%d) doesn match face rectangle size(%d)!", 495 faceScores.length, faceRectangles.length)); 496 } 497 498 // To be safe, make number of faces is the minimal of all face info metadata length. 499 int numFaces = Math.min(faceScores.length, faceRectangles.length); 500 // Face id and landmarks are only required by FULL mode. 501 int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS); 502 int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS); 503 if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) { 504 if (faceIds == null || faceLandmarks == null) { 505 Log.w(TAG, "Expect face ids and landmarks to be non-null for FULL mode," + 506 "fallback to SIMPLE mode"); 507 faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE; 508 } else { 509 if (faceIds.length != numFaces || 510 faceLandmarks.length != numFaces * FACE_LANDMARK_SIZE) { 511 Log.w(TAG, String.format("Face id size(%d), or face landmark size(%d) don't" + 512 "match face number(%d)!", 513 faceIds.length, faceLandmarks.length * FACE_LANDMARK_SIZE, numFaces)); 514 } 515 // To be safe, make number of faces is the minimal of all face info metadata length. 516 numFaces = Math.min(numFaces, faceIds.length); 517 numFaces = Math.min(numFaces, faceLandmarks.length / FACE_LANDMARK_SIZE); 518 } 519 } 520 521 ArrayList<Face> faceList = new ArrayList<Face>(); 522 if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE) { 523 for (int i = 0; i < numFaces; i++) { 524 if (faceScores[i] <= Face.SCORE_MAX && 525 faceScores[i] >= Face.SCORE_MIN) { 526 faceList.add(new Face(faceRectangles[i], faceScores[i])); 527 } 528 } 529 } else { 530 // CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL 531 for (int i = 0; i < numFaces; i++) { 532 if (faceScores[i] <= Face.SCORE_MAX && 533 faceScores[i] >= Face.SCORE_MIN && 534 faceIds[i] >= 0) { 535 Point leftEye = new Point(faceLandmarks[i*6], faceLandmarks[i*6+1]); 536 Point rightEye = new Point(faceLandmarks[i*6+2], faceLandmarks[i*6+3]); 537 Point mouth = new Point(faceLandmarks[i*6+4], faceLandmarks[i*6+5]); 538 Face face = new Face(faceRectangles[i], faceScores[i], faceIds[i], 539 leftEye, rightEye, mouth); 540 faceList.add(face); 541 } 542 } 543 } 544 Face[] faces = new Face[faceList.size()]; 545 faceList.toArray(faces); 546 return faces; 547 } 548 549 // Face rectangles are defined as (left, top, right, bottom) instead of 550 // (left, top, width, height) at the native level, so the normal Rect 551 // conversion that does (l, t, w, h) -> (l, t, r, b) is unnecessary. Undo 552 // that conversion here for just the faces. 553 private Rect[] fixFaceRectangles() { 554 Rect[] faceRectangles = getBase(CaptureResult.STATISTICS_FACE_RECTANGLES); 555 if (faceRectangles == null) return null; 556 557 Rect[] fixedFaceRectangles = new Rect[faceRectangles.length]; 558 for (int i = 0; i < faceRectangles.length; i++) { 559 fixedFaceRectangles[i] = new Rect( 560 faceRectangles[i].left, 561 faceRectangles[i].top, 562 faceRectangles[i].right - faceRectangles[i].left, 563 faceRectangles[i].bottom - faceRectangles[i].top); 564 } 565 return fixedFaceRectangles; 566 } 567 568 private <T> void setBase(Key<T> key, T value) { 569 int tag = key.getTag(); 570 571 if (value == null) { 572 writeValues(tag, null); 573 return; 574 } 575 576 int nativeType = getNativeType(tag); 577 578 int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true); 579 580 // TODO: Optimization. Cache the byte[] and reuse if the size is big enough. 581 byte[] values = new byte[size]; 582 583 ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); 584 packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false); 585 586 writeValues(tag, values); 587 } 588 589 // Set the camera metadata override. 590 private <T> boolean setOverride(Key<T> key, T value) { 591 if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)) { 592 return setAvailableFormats((int[]) value); 593 } 594 595 // For other keys, set() falls back to setBase(). 596 return false; 597 } 598 599 private boolean setAvailableFormats(int[] value) { 600 int[] availableFormat = value; 601 if (value == null) { 602 // Let setBase() to handle the null value case. 603 return false; 604 } 605 606 int[] newValues = new int[availableFormat.length]; 607 for (int i = 0; i < availableFormat.length; i++) { 608 newValues[i] = availableFormat[i]; 609 if (availableFormat[i] == ImageFormat.JPEG) { 610 newValues[i] = NATIVE_JPEG_FORMAT; 611 } 612 } 613 614 setBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS, newValues); 615 return true; 616 } 617 618 private long mMetadataPtr; // native CameraMetadata* 619 620 private native long nativeAllocate(); 621 private native long nativeAllocateCopy(CameraMetadataNative other) 622 throws NullPointerException; 623 624 private native synchronized void nativeWriteToParcel(Parcel dest); 625 private native synchronized void nativeReadFromParcel(Parcel source); 626 private native synchronized void nativeSwap(CameraMetadataNative other) 627 throws NullPointerException; 628 private native synchronized void nativeClose(); 629 private native synchronized boolean nativeIsEmpty(); 630 private native synchronized int nativeGetEntryCount(); 631 632 private native synchronized byte[] nativeReadValues(int tag); 633 private native synchronized void nativeWriteValues(int tag, byte[] src); 634 635 private static native int nativeGetTagFromKey(String keyName) 636 throws IllegalArgumentException; 637 private static native int nativeGetTypeFromTag(int tag) 638 throws IllegalArgumentException; 639 private static native void nativeClassInit(); 640 641 /** 642 * <p>Perform a 0-copy swap of the internal metadata with another object.</p> 643 * 644 * <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p> 645 * 646 * @param other Metadata to swap with 647 * @throws NullPointerException if other was null 648 * @hide 649 */ 650 public void swap(CameraMetadataNative other) { 651 nativeSwap(other); 652 } 653 654 /** 655 * @hide 656 */ 657 public int getEntryCount() { 658 return nativeGetEntryCount(); 659 } 660 661 /** 662 * Does this metadata contain at least 1 entry? 663 * 664 * @hide 665 */ 666 public boolean isEmpty() { 667 return nativeIsEmpty(); 668 } 669 670 /** 671 * Convert a key string into the equivalent native tag. 672 * 673 * @throws IllegalArgumentException if the key was not recognized 674 * @throws NullPointerException if the key was null 675 * 676 * @hide 677 */ 678 public static int getTag(String key) { 679 return nativeGetTagFromKey(key); 680 } 681 682 /** 683 * Get the underlying native type for a tag. 684 * 685 * @param tag An integer tag, see e.g. {@link #getTag} 686 * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE} 687 * 688 * @hide 689 */ 690 public static int getNativeType(int tag) { 691 return nativeGetTypeFromTag(tag); 692 } 693 694 /** 695 * <p>Updates the existing entry for tag with the new bytes pointed by src, erasing 696 * the entry if src was null.</p> 697 * 698 * <p>An empty array can be passed in to update the entry to 0 elements.</p> 699 * 700 * @param tag An integer tag, see e.g. {@link #getTag} 701 * @param src An array of bytes, or null to erase the entry 702 * 703 * @hide 704 */ 705 public void writeValues(int tag, byte[] src) { 706 nativeWriteValues(tag, src); 707 } 708 709 /** 710 * <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize 711 * the data properly.</p> 712 * 713 * <p>An empty array can be returned to denote an existing entry with 0 elements.</p> 714 * 715 * @param tag An integer tag, see e.g. {@link #getTag} 716 * 717 * @return {@code null} if there were 0 entries for this tag, a byte[] otherwise. 718 * @hide 719 */ 720 public byte[] readValues(int tag) { 721 // TODO: Optimization. Native code returns a ByteBuffer instead. 722 return nativeReadValues(tag); 723 } 724 725 @Override 726 protected void finalize() throws Throwable { 727 try { 728 close(); 729 } finally { 730 super.finalize(); 731 } 732 } 733 734 private static final HashMap<Class<? extends Enum>, int[]> sEnumValues = 735 new HashMap<Class<? extends Enum>, int[]>(); 736 /** 737 * Register a non-sequential set of values to be used with the pack/unpack functions. 738 * This enables get/set to correctly marshal the enum into a value that is C-compatible. 739 * 740 * @param enumType The class for an enum 741 * @param values A list of values mapping to the ordinals of the enum 742 * 743 * @hide 744 */ 745 public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) { 746 if (enumType.getEnumConstants().length != values.length) { 747 throw new IllegalArgumentException( 748 "Expected values array to be the same size as the enumTypes values " 749 + values.length + " for type " + enumType); 750 } 751 if (VERBOSE) { 752 Log.v(TAG, "Registered enum values for type " + enumType + " values"); 753 } 754 755 sEnumValues.put(enumType, values); 756 } 757 758 /** 759 * Get the numeric value from an enum. This is usually the same as the ordinal value for 760 * enums that have fully sequential values, although for C-style enums the range of values 761 * may not map 1:1. 762 * 763 * @param enumValue Enum instance 764 * @return Int guaranteed to be ABI-compatible with the C enum equivalent 765 */ 766 private static <T extends Enum<T>> int getEnumValue(T enumValue) { 767 int[] values; 768 values = sEnumValues.get(enumValue.getClass()); 769 770 int ordinal = enumValue.ordinal(); 771 if (values != null) { 772 return values[ordinal]; 773 } 774 775 return ordinal; 776 } 777 778 /** 779 * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method. 780 * 781 * @param enumType Class of the enum we want to find 782 * @param value The numeric value of the enum 783 * @return An instance of the enum 784 */ 785 private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) { 786 int ordinal; 787 788 int[] registeredValues = sEnumValues.get(enumType); 789 if (registeredValues != null) { 790 ordinal = -1; 791 792 for (int i = 0; i < registeredValues.length; ++i) { 793 if (registeredValues[i] == value) { 794 ordinal = i; 795 break; 796 } 797 } 798 } else { 799 ordinal = value; 800 } 801 802 T[] values = enumType.getEnumConstants(); 803 804 if (ordinal < 0 || ordinal >= values.length) { 805 throw new IllegalArgumentException( 806 String.format( 807 "Argument 'value' (%d) was not a valid enum value for type %s " 808 + "(registered? %b)", 809 value, 810 enumType, (registeredValues != null))); 811 } 812 813 return values[ordinal]; 814 } 815 816 static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new 817 HashMap<Class<?>, MetadataMarshalClass<?>>(); 818 819 private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) { 820 sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler); 821 } 822 823 @SuppressWarnings("unchecked") 824 private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) { 825 MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type); 826 827 if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) { 828 throw new UnsupportedOperationException("Unsupported type " + nativeType + 829 " to be marshalled to/from a " + type); 830 } 831 832 return marshaler; 833 } 834 835 /** 836 * We use a class initializer to allow the native code to cache some field offsets 837 */ 838 static { 839 nativeClassInit(); 840 841 if (VERBOSE) { 842 Log.v(TAG, "Shall register metadata marshalers"); 843 } 844 845 // load built-in marshallers 846 registerMarshaler(new MetadataMarshalRect()); 847 registerMarshaler(new MetadataMarshalSize()); 848 registerMarshaler(new MetadataMarshalString()); 849 850 if (VERBOSE) { 851 Log.v(TAG, "Registered metadata marshalers"); 852 } 853 } 854 855} 856