CameraMetadataNative.java revision b7bfdc7cf7f45805e8e7ebea77a15051b8ad3e8d
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 == CameraCharacteristics.SCALER_AVAILABLE_FORMATS) { 447 return (T) getAvailableFormats(); 448 } else if (key == CaptureResult.STATISTICS_FACES) { 449 return (T) getFaces(); 450 } 451 452 // For other keys, get() falls back to getBase() 453 return null; 454 } 455 456 private int[] getAvailableFormats() { 457 int[] availableFormats = getBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS); 458 for (int i = 0; i < availableFormats.length; i++) { 459 // JPEG has different value between native and managed side, need override. 460 if (availableFormats[i] == NATIVE_JPEG_FORMAT) { 461 availableFormats[i] = ImageFormat.JPEG; 462 } 463 } 464 return availableFormats; 465 } 466 467 private Face[] getFaces() { 468 final int FACE_LANDMARK_SIZE = 6; 469 470 Integer faceDetectMode = getBase(CaptureResult.STATISTICS_FACE_DETECT_MODE); 471 if (faceDetectMode == null) { 472 throw new AssertionError("Expect non-null face detect mode"); 473 } 474 475 if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) { 476 return new Face[0]; 477 } 478 if (faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE && 479 faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) { 480 throw new AssertionError("Unknown face detect mode: " + faceDetectMode); 481 } 482 483 // Face scores and rectangles are required by SIMPLE and FULL mode. 484 byte[] faceScores = getBase(CaptureResult.STATISTICS_FACE_SCORES); 485 Rect[] faceRectangles = getBase(CaptureResult.STATISTICS_FACE_RECTANGLES); 486 if (faceScores == null || faceRectangles == null) { 487 throw new AssertionError("Expect face scores and rectangles to be non-null"); 488 } else if (faceScores.length != faceRectangles.length) { 489 throw new AssertionError( 490 String.format("Face score size(%d) doesn match face rectangle size(%d)!", 491 faceScores.length, faceRectangles.length)); 492 } 493 494 // Face id and landmarks are only required by FULL mode. 495 int[] faceIds = getBase(CaptureResult.STATISTICS_FACE_IDS); 496 int[] faceLandmarks = getBase(CaptureResult.STATISTICS_FACE_LANDMARKS); 497 int numFaces = faceScores.length; 498 if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) { 499 if (faceIds == null || faceLandmarks == null) { 500 throw new AssertionError("Expect face ids and landmarks to be non-null for " + 501 "FULL mode"); 502 } else if (faceIds.length != numFaces || 503 faceLandmarks.length != numFaces * FACE_LANDMARK_SIZE) { 504 throw new AssertionError( 505 String.format("Face id size(%d), or face landmark size(%d) don't match " + 506 "face number(%d)!", 507 faceIds.length, faceLandmarks.length * FACE_LANDMARK_SIZE, 508 numFaces)); 509 } 510 } 511 512 Face[] faces = new Face[numFaces]; 513 if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE) { 514 for (int i = 0; i < numFaces; i++) { 515 faces[i] = new Face(faceRectangles[i], faceScores[i]); 516 } 517 } else { 518 // CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL 519 for (int i = 0; i < numFaces; i++) { 520 Point leftEye = new Point(faceLandmarks[i*6], faceLandmarks[i*6+1]); 521 Point rightEye = new Point(faceLandmarks[i*6+2], faceLandmarks[i*6+3]); 522 Point mouth = new Point(faceLandmarks[i*6+4], faceLandmarks[i*6+5]); 523 faces[i] = new Face(faceRectangles[i], faceScores[i], faceIds[i], 524 leftEye, rightEye, mouth); 525 } 526 } 527 return faces; 528 } 529 530 private <T> void setBase(Key<T> key, T value) { 531 int tag = key.getTag(); 532 533 if (value == null) { 534 writeValues(tag, null); 535 return; 536 } 537 538 int nativeType = getNativeType(tag); 539 540 int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true); 541 542 // TODO: Optimization. Cache the byte[] and reuse if the size is big enough. 543 byte[] values = new byte[size]; 544 545 ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); 546 packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false); 547 548 writeValues(tag, values); 549 } 550 551 // Set the camera metadata override. 552 private <T> boolean setOverride(Key<T> key, T value) { 553 if (key == CameraCharacteristics.SCALER_AVAILABLE_FORMATS) { 554 return setAvailableFormats((int[]) value); 555 } 556 557 // For other keys, set() falls back to setBase(). 558 return false; 559 } 560 561 private boolean setAvailableFormats(int[] value) { 562 int[] availableFormat = value; 563 if (value == null) { 564 // Let setBase() to handle the null value case. 565 return false; 566 } 567 568 int[] newValues = new int[availableFormat.length]; 569 for (int i = 0; i < availableFormat.length; i++) { 570 newValues[i] = availableFormat[i]; 571 if (availableFormat[i] == ImageFormat.JPEG) { 572 newValues[i] = NATIVE_JPEG_FORMAT; 573 } 574 } 575 576 setBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS, newValues); 577 return true; 578 } 579 580 private long mMetadataPtr; // native CameraMetadata* 581 582 private native long nativeAllocate(); 583 private native long nativeAllocateCopy(CameraMetadataNative other) 584 throws NullPointerException; 585 586 private native synchronized void nativeWriteToParcel(Parcel dest); 587 private native synchronized void nativeReadFromParcel(Parcel source); 588 private native synchronized void nativeSwap(CameraMetadataNative other) 589 throws NullPointerException; 590 private native synchronized void nativeClose(); 591 private native synchronized boolean nativeIsEmpty(); 592 private native synchronized int nativeGetEntryCount(); 593 594 private native synchronized byte[] nativeReadValues(int tag); 595 private native synchronized void nativeWriteValues(int tag, byte[] src); 596 597 private static native int nativeGetTagFromKey(String keyName) 598 throws IllegalArgumentException; 599 private static native int nativeGetTypeFromTag(int tag) 600 throws IllegalArgumentException; 601 private static native void nativeClassInit(); 602 603 /** 604 * <p>Perform a 0-copy swap of the internal metadata with another object.</p> 605 * 606 * <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p> 607 * 608 * @param other Metadata to swap with 609 * @throws NullPointerException if other was null 610 * @hide 611 */ 612 public void swap(CameraMetadataNative other) { 613 nativeSwap(other); 614 } 615 616 /** 617 * @hide 618 */ 619 public int getEntryCount() { 620 return nativeGetEntryCount(); 621 } 622 623 /** 624 * Does this metadata contain at least 1 entry? 625 * 626 * @hide 627 */ 628 public boolean isEmpty() { 629 return nativeIsEmpty(); 630 } 631 632 /** 633 * Convert a key string into the equivalent native tag. 634 * 635 * @throws IllegalArgumentException if the key was not recognized 636 * @throws NullPointerException if the key was null 637 * 638 * @hide 639 */ 640 public static int getTag(String key) { 641 return nativeGetTagFromKey(key); 642 } 643 644 /** 645 * Get the underlying native type for a tag. 646 * 647 * @param tag An integer tag, see e.g. {@link #getTag} 648 * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE} 649 * 650 * @hide 651 */ 652 public static int getNativeType(int tag) { 653 return nativeGetTypeFromTag(tag); 654 } 655 656 /** 657 * <p>Updates the existing entry for tag with the new bytes pointed by src, erasing 658 * the entry if src was null.</p> 659 * 660 * <p>An empty array can be passed in to update the entry to 0 elements.</p> 661 * 662 * @param tag An integer tag, see e.g. {@link #getTag} 663 * @param src An array of bytes, or null to erase the entry 664 * 665 * @hide 666 */ 667 public void writeValues(int tag, byte[] src) { 668 nativeWriteValues(tag, src); 669 } 670 671 /** 672 * <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize 673 * the data properly.</p> 674 * 675 * <p>An empty array can be returned to denote an existing entry with 0 elements.</p> 676 * 677 * @param tag An integer tag, see e.g. {@link #getTag} 678 * 679 * @return {@code null} if there were 0 entries for this tag, a byte[] otherwise. 680 * @hide 681 */ 682 public byte[] readValues(int tag) { 683 // TODO: Optimization. Native code returns a ByteBuffer instead. 684 return nativeReadValues(tag); 685 } 686 687 @Override 688 protected void finalize() throws Throwable { 689 try { 690 close(); 691 } finally { 692 super.finalize(); 693 } 694 } 695 696 private static final HashMap<Class<? extends Enum>, int[]> sEnumValues = 697 new HashMap<Class<? extends Enum>, int[]>(); 698 /** 699 * Register a non-sequential set of values to be used with the pack/unpack functions. 700 * This enables get/set to correctly marshal the enum into a value that is C-compatible. 701 * 702 * @param enumType The class for an enum 703 * @param values A list of values mapping to the ordinals of the enum 704 * 705 * @hide 706 */ 707 public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) { 708 if (enumType.getEnumConstants().length != values.length) { 709 throw new IllegalArgumentException( 710 "Expected values array to be the same size as the enumTypes values " 711 + values.length + " for type " + enumType); 712 } 713 if (VERBOSE) { 714 Log.v(TAG, "Registered enum values for type " + enumType + " values"); 715 } 716 717 sEnumValues.put(enumType, values); 718 } 719 720 /** 721 * Get the numeric value from an enum. This is usually the same as the ordinal value for 722 * enums that have fully sequential values, although for C-style enums the range of values 723 * may not map 1:1. 724 * 725 * @param enumValue Enum instance 726 * @return Int guaranteed to be ABI-compatible with the C enum equivalent 727 */ 728 private static <T extends Enum<T>> int getEnumValue(T enumValue) { 729 int[] values; 730 values = sEnumValues.get(enumValue.getClass()); 731 732 int ordinal = enumValue.ordinal(); 733 if (values != null) { 734 return values[ordinal]; 735 } 736 737 return ordinal; 738 } 739 740 /** 741 * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method. 742 * 743 * @param enumType Class of the enum we want to find 744 * @param value The numeric value of the enum 745 * @return An instance of the enum 746 */ 747 private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) { 748 int ordinal; 749 750 int[] registeredValues = sEnumValues.get(enumType); 751 if (registeredValues != null) { 752 ordinal = -1; 753 754 for (int i = 0; i < registeredValues.length; ++i) { 755 if (registeredValues[i] == value) { 756 ordinal = i; 757 break; 758 } 759 } 760 } else { 761 ordinal = value; 762 } 763 764 T[] values = enumType.getEnumConstants(); 765 766 if (ordinal < 0 || ordinal >= values.length) { 767 throw new IllegalArgumentException( 768 String.format( 769 "Argument 'value' (%d) was not a valid enum value for type %s " 770 + "(registered? %b)", 771 value, 772 enumType, (registeredValues != null))); 773 } 774 775 return values[ordinal]; 776 } 777 778 static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new 779 HashMap<Class<?>, MetadataMarshalClass<?>>(); 780 781 private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) { 782 sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler); 783 } 784 785 @SuppressWarnings("unchecked") 786 private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) { 787 MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type); 788 789 if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) { 790 throw new UnsupportedOperationException("Unsupported type " + nativeType + 791 " to be marshalled to/from a " + type); 792 } 793 794 return marshaler; 795 } 796 797 /** 798 * We use a class initializer to allow the native code to cache some field offsets 799 */ 800 static { 801 nativeClassInit(); 802 803 if (VERBOSE) { 804 Log.v(TAG, "Shall register metadata marshalers"); 805 } 806 807 // load built-in marshallers 808 registerMarshaler(new MetadataMarshalRect()); 809 registerMarshaler(new MetadataMarshalSize()); 810 registerMarshaler(new MetadataMarshalString()); 811 812 if (VERBOSE) { 813 Log.v(TAG, "Registered metadata marshalers"); 814 } 815 } 816 817} 818