CameraMetadataNative.java revision 58216c2650c9536f93467d6ab347ca3f7720dedd
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 Face[] faces = new Face[numFaces]; 522 if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE) { 523 for (int i = 0; i < numFaces; i++) { 524 faces[i] = new Face(faceRectangles[i], faceScores[i]); 525 } 526 } else { 527 // CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL 528 for (int i = 0; i < numFaces; i++) { 529 Point leftEye = new Point(faceLandmarks[i*6], faceLandmarks[i*6+1]); 530 Point rightEye = new Point(faceLandmarks[i*6+2], faceLandmarks[i*6+3]); 531 Point mouth = new Point(faceLandmarks[i*6+4], faceLandmarks[i*6+5]); 532 faces[i] = new Face(faceRectangles[i], faceScores[i], faceIds[i], 533 leftEye, rightEye, mouth); 534 } 535 } 536 return faces; 537 } 538 539 // Face rectangles are defined as (left, top, right, bottom) instead of 540 // (left, top, width, height) at the native level, so the normal Rect 541 // conversion that does (l, t, w, h) -> (l, t, r, b) is unnecessary. Undo 542 // that conversion here for just the faces. 543 private Rect[] fixFaceRectangles() { 544 Rect[] faceRectangles = getBase(CaptureResult.STATISTICS_FACE_RECTANGLES); 545 if (faceRectangles == null) return null; 546 547 Rect[] fixedFaceRectangles = new Rect[faceRectangles.length]; 548 for (int i = 0; i < faceRectangles.length; i++) { 549 fixedFaceRectangles[i] = new Rect( 550 faceRectangles[i].left, 551 faceRectangles[i].top, 552 faceRectangles[i].right - faceRectangles[i].left, 553 faceRectangles[i].bottom - faceRectangles[i].top); 554 } 555 return fixedFaceRectangles; 556 } 557 558 private <T> void setBase(Key<T> key, T value) { 559 int tag = key.getTag(); 560 561 if (value == null) { 562 writeValues(tag, null); 563 return; 564 } 565 566 int nativeType = getNativeType(tag); 567 568 int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true); 569 570 // TODO: Optimization. Cache the byte[] and reuse if the size is big enough. 571 byte[] values = new byte[size]; 572 573 ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); 574 packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false); 575 576 writeValues(tag, values); 577 } 578 579 // Set the camera metadata override. 580 private <T> boolean setOverride(Key<T> key, T value) { 581 if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)) { 582 return setAvailableFormats((int[]) value); 583 } 584 585 // For other keys, set() falls back to setBase(). 586 return false; 587 } 588 589 private boolean setAvailableFormats(int[] value) { 590 int[] availableFormat = value; 591 if (value == null) { 592 // Let setBase() to handle the null value case. 593 return false; 594 } 595 596 int[] newValues = new int[availableFormat.length]; 597 for (int i = 0; i < availableFormat.length; i++) { 598 newValues[i] = availableFormat[i]; 599 if (availableFormat[i] == ImageFormat.JPEG) { 600 newValues[i] = NATIVE_JPEG_FORMAT; 601 } 602 } 603 604 setBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS, newValues); 605 return true; 606 } 607 608 private long mMetadataPtr; // native CameraMetadata* 609 610 private native long nativeAllocate(); 611 private native long nativeAllocateCopy(CameraMetadataNative other) 612 throws NullPointerException; 613 614 private native synchronized void nativeWriteToParcel(Parcel dest); 615 private native synchronized void nativeReadFromParcel(Parcel source); 616 private native synchronized void nativeSwap(CameraMetadataNative other) 617 throws NullPointerException; 618 private native synchronized void nativeClose(); 619 private native synchronized boolean nativeIsEmpty(); 620 private native synchronized int nativeGetEntryCount(); 621 622 private native synchronized byte[] nativeReadValues(int tag); 623 private native synchronized void nativeWriteValues(int tag, byte[] src); 624 625 private static native int nativeGetTagFromKey(String keyName) 626 throws IllegalArgumentException; 627 private static native int nativeGetTypeFromTag(int tag) 628 throws IllegalArgumentException; 629 private static native void nativeClassInit(); 630 631 /** 632 * <p>Perform a 0-copy swap of the internal metadata with another object.</p> 633 * 634 * <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p> 635 * 636 * @param other Metadata to swap with 637 * @throws NullPointerException if other was null 638 * @hide 639 */ 640 public void swap(CameraMetadataNative other) { 641 nativeSwap(other); 642 } 643 644 /** 645 * @hide 646 */ 647 public int getEntryCount() { 648 return nativeGetEntryCount(); 649 } 650 651 /** 652 * Does this metadata contain at least 1 entry? 653 * 654 * @hide 655 */ 656 public boolean isEmpty() { 657 return nativeIsEmpty(); 658 } 659 660 /** 661 * Convert a key string into the equivalent native tag. 662 * 663 * @throws IllegalArgumentException if the key was not recognized 664 * @throws NullPointerException if the key was null 665 * 666 * @hide 667 */ 668 public static int getTag(String key) { 669 return nativeGetTagFromKey(key); 670 } 671 672 /** 673 * Get the underlying native type for a tag. 674 * 675 * @param tag An integer tag, see e.g. {@link #getTag} 676 * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE} 677 * 678 * @hide 679 */ 680 public static int getNativeType(int tag) { 681 return nativeGetTypeFromTag(tag); 682 } 683 684 /** 685 * <p>Updates the existing entry for tag with the new bytes pointed by src, erasing 686 * the entry if src was null.</p> 687 * 688 * <p>An empty array can be passed in to update the entry to 0 elements.</p> 689 * 690 * @param tag An integer tag, see e.g. {@link #getTag} 691 * @param src An array of bytes, or null to erase the entry 692 * 693 * @hide 694 */ 695 public void writeValues(int tag, byte[] src) { 696 nativeWriteValues(tag, src); 697 } 698 699 /** 700 * <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize 701 * the data properly.</p> 702 * 703 * <p>An empty array can be returned to denote an existing entry with 0 elements.</p> 704 * 705 * @param tag An integer tag, see e.g. {@link #getTag} 706 * 707 * @return {@code null} if there were 0 entries for this tag, a byte[] otherwise. 708 * @hide 709 */ 710 public byte[] readValues(int tag) { 711 // TODO: Optimization. Native code returns a ByteBuffer instead. 712 return nativeReadValues(tag); 713 } 714 715 @Override 716 protected void finalize() throws Throwable { 717 try { 718 close(); 719 } finally { 720 super.finalize(); 721 } 722 } 723 724 private static final HashMap<Class<? extends Enum>, int[]> sEnumValues = 725 new HashMap<Class<? extends Enum>, int[]>(); 726 /** 727 * Register a non-sequential set of values to be used with the pack/unpack functions. 728 * This enables get/set to correctly marshal the enum into a value that is C-compatible. 729 * 730 * @param enumType The class for an enum 731 * @param values A list of values mapping to the ordinals of the enum 732 * 733 * @hide 734 */ 735 public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) { 736 if (enumType.getEnumConstants().length != values.length) { 737 throw new IllegalArgumentException( 738 "Expected values array to be the same size as the enumTypes values " 739 + values.length + " for type " + enumType); 740 } 741 if (VERBOSE) { 742 Log.v(TAG, "Registered enum values for type " + enumType + " values"); 743 } 744 745 sEnumValues.put(enumType, values); 746 } 747 748 /** 749 * Get the numeric value from an enum. This is usually the same as the ordinal value for 750 * enums that have fully sequential values, although for C-style enums the range of values 751 * may not map 1:1. 752 * 753 * @param enumValue Enum instance 754 * @return Int guaranteed to be ABI-compatible with the C enum equivalent 755 */ 756 private static <T extends Enum<T>> int getEnumValue(T enumValue) { 757 int[] values; 758 values = sEnumValues.get(enumValue.getClass()); 759 760 int ordinal = enumValue.ordinal(); 761 if (values != null) { 762 return values[ordinal]; 763 } 764 765 return ordinal; 766 } 767 768 /** 769 * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method. 770 * 771 * @param enumType Class of the enum we want to find 772 * @param value The numeric value of the enum 773 * @return An instance of the enum 774 */ 775 private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) { 776 int ordinal; 777 778 int[] registeredValues = sEnumValues.get(enumType); 779 if (registeredValues != null) { 780 ordinal = -1; 781 782 for (int i = 0; i < registeredValues.length; ++i) { 783 if (registeredValues[i] == value) { 784 ordinal = i; 785 break; 786 } 787 } 788 } else { 789 ordinal = value; 790 } 791 792 T[] values = enumType.getEnumConstants(); 793 794 if (ordinal < 0 || ordinal >= values.length) { 795 throw new IllegalArgumentException( 796 String.format( 797 "Argument 'value' (%d) was not a valid enum value for type %s " 798 + "(registered? %b)", 799 value, 800 enumType, (registeredValues != null))); 801 } 802 803 return values[ordinal]; 804 } 805 806 static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new 807 HashMap<Class<?>, MetadataMarshalClass<?>>(); 808 809 private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) { 810 sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler); 811 } 812 813 @SuppressWarnings("unchecked") 814 private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) { 815 MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type); 816 817 if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) { 818 throw new UnsupportedOperationException("Unsupported type " + nativeType + 819 " to be marshalled to/from a " + type); 820 } 821 822 return marshaler; 823 } 824 825 /** 826 * We use a class initializer to allow the native code to cache some field offsets 827 */ 828 static { 829 nativeClassInit(); 830 831 if (VERBOSE) { 832 Log.v(TAG, "Shall register metadata marshalers"); 833 } 834 835 // load built-in marshallers 836 registerMarshaler(new MetadataMarshalRect()); 837 registerMarshaler(new MetadataMarshalSize()); 838 registerMarshaler(new MetadataMarshalString()); 839 840 if (VERBOSE) { 841 Log.v(TAG, "Registered metadata marshalers"); 842 } 843 } 844 845} 846