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