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.CaptureRequest;
24import android.hardware.camera2.CaptureResult;
25import android.hardware.camera2.marshal.Marshaler;
26import android.hardware.camera2.marshal.MarshalQueryable;
27import android.hardware.camera2.marshal.MarshalRegistry;
28import android.hardware.camera2.marshal.impl.MarshalQueryableArray;
29import android.hardware.camera2.marshal.impl.MarshalQueryableBoolean;
30import android.hardware.camera2.marshal.impl.MarshalQueryableBlackLevelPattern;
31import android.hardware.camera2.marshal.impl.MarshalQueryableColorSpaceTransform;
32import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
33import android.hardware.camera2.marshal.impl.MarshalQueryableHighSpeedVideoConfiguration;
34import android.hardware.camera2.marshal.impl.MarshalQueryableMeteringRectangle;
35import android.hardware.camera2.marshal.impl.MarshalQueryableNativeByteToInteger;
36import android.hardware.camera2.marshal.impl.MarshalQueryablePair;
37import android.hardware.camera2.marshal.impl.MarshalQueryableParcelable;
38import android.hardware.camera2.marshal.impl.MarshalQueryablePrimitive;
39import android.hardware.camera2.marshal.impl.MarshalQueryableRange;
40import android.hardware.camera2.marshal.impl.MarshalQueryableRect;
41import android.hardware.camera2.marshal.impl.MarshalQueryableReprocessFormatsMap;
42import android.hardware.camera2.marshal.impl.MarshalQueryableRggbChannelVector;
43import android.hardware.camera2.marshal.impl.MarshalQueryableSize;
44import android.hardware.camera2.marshal.impl.MarshalQueryableSizeF;
45import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfiguration;
46import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration;
47import android.hardware.camera2.marshal.impl.MarshalQueryableString;
48import android.hardware.camera2.params.Face;
49import android.hardware.camera2.params.HighSpeedVideoConfiguration;
50import android.hardware.camera2.params.LensShadingMap;
51import android.hardware.camera2.params.ReprocessFormatsMap;
52import android.hardware.camera2.params.StreamConfiguration;
53import android.hardware.camera2.params.StreamConfigurationDuration;
54import android.hardware.camera2.params.StreamConfigurationMap;
55import android.hardware.camera2.params.TonemapCurve;
56import android.hardware.camera2.utils.TypeReference;
57import android.location.Location;
58import android.location.LocationManager;
59import android.os.Parcelable;
60import android.os.Parcel;
61import android.os.ServiceSpecificException;
62import android.util.Log;
63import android.util.Size;
64
65import com.android.internal.util.Preconditions;
66
67import java.io.IOException;
68import java.nio.ByteBuffer;
69import java.nio.ByteOrder;
70import java.util.ArrayList;
71import java.util.HashMap;
72
73/**
74 * Implementation of camera metadata marshal/unmarshal across Binder to
75 * the camera service
76 */
77public class CameraMetadataNative implements Parcelable {
78
79    public static class Key<T> {
80        private boolean mHasTag;
81        private int mTag;
82        private final Class<T> mType;
83        private final TypeReference<T> mTypeReference;
84        private final String mName;
85        private final int mHash;
86        /**
87         * Visible for testing only.
88         *
89         * <p>Use the CameraCharacteristics.Key, CaptureResult.Key, or CaptureRequest.Key
90         * for application code or vendor-extended keys.</p>
91         */
92        public Key(String name, Class<T> type) {
93            if (name == null) {
94                throw new NullPointerException("Key needs a valid name");
95            } else if (type == null) {
96                throw new NullPointerException("Type needs to be non-null");
97            }
98            mName = name;
99            mType = type;
100            mTypeReference = TypeReference.createSpecializedTypeReference(type);
101            mHash = mName.hashCode() ^ mTypeReference.hashCode();
102        }
103
104        /**
105         * Visible for testing only.
106         *
107         * <p>Use the CameraCharacteristics.Key, CaptureResult.Key, or CaptureRequest.Key
108         * for application code or vendor-extended keys.</p>
109         */
110        @SuppressWarnings("unchecked")
111        public Key(String name, TypeReference<T> typeReference) {
112            if (name == null) {
113                throw new NullPointerException("Key needs a valid name");
114            } else if (typeReference == null) {
115                throw new NullPointerException("TypeReference needs to be non-null");
116            }
117            mName = name;
118            mType = (Class<T>)typeReference.getRawType();
119            mTypeReference = typeReference;
120            mHash = mName.hashCode() ^ mTypeReference.hashCode();
121        }
122
123        /**
124         * Return a camelCase, period separated name formatted like:
125         * {@code "root.section[.subsections].name"}.
126         *
127         * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
128         * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
129         *
130         * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
131         * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
132         * specific key might look like {@code "com.google.nexus.data.private"}.</p>
133         *
134         * @return String representation of the key name
135         */
136        public final String getName() {
137            return mName;
138        }
139
140        /**
141         * {@inheritDoc}
142         */
143        @Override
144        public final int hashCode() {
145            return mHash;
146        }
147
148        /**
149         * Compare this key against other native keys, request keys, result keys, and
150         * characteristics keys.
151         *
152         * <p>Two keys are considered equal if their name and type reference are equal.</p>
153         *
154         * <p>Note that the equality against non-native keys is one-way. A native key may be equal
155         * to a result key; but that same result key will not be equal to a native key.</p>
156         */
157        @SuppressWarnings("rawtypes")
158        @Override
159        public final boolean equals(Object o) {
160            if (this == o) {
161                return true;
162            }
163
164            if (o == null || this.hashCode() != o.hashCode()) {
165                return false;
166            }
167
168            Key<?> lhs;
169
170            if (o instanceof CaptureResult.Key) {
171                lhs = ((CaptureResult.Key)o).getNativeKey();
172            } else if (o instanceof CaptureRequest.Key) {
173                lhs = ((CaptureRequest.Key)o).getNativeKey();
174            } else if (o instanceof CameraCharacteristics.Key) {
175                lhs = ((CameraCharacteristics.Key)o).getNativeKey();
176            } else if ((o instanceof Key)) {
177                lhs = (Key<?>)o;
178            } else {
179                return false;
180            }
181
182            return mName.equals(lhs.mName) && mTypeReference.equals(lhs.mTypeReference);
183        }
184
185        /**
186         * <p>
187         * Get the tag corresponding to this key. This enables insertion into the
188         * native metadata.
189         * </p>
190         *
191         * <p>This value is looked up the first time, and cached subsequently.</p>
192         *
193         * @return The tag numeric value corresponding to the string
194         */
195        public final int getTag() {
196            if (!mHasTag) {
197                mTag = CameraMetadataNative.getTag(mName);
198                mHasTag = true;
199            }
200            return mTag;
201        }
202
203        /**
204         * Get the raw class backing the type {@code T} for this key.
205         *
206         * <p>The distinction is only important if {@code T} is a generic, e.g.
207         * {@code Range<Integer>} since the nested type will be erased.</p>
208         */
209        public final Class<T> getType() {
210            // TODO: remove this; other places should use #getTypeReference() instead
211            return mType;
212        }
213
214        /**
215         * Get the type reference backing the type {@code T} for this key.
216         *
217         * <p>The distinction is only important if {@code T} is a generic, e.g.
218         * {@code Range<Integer>} since the nested type will be retained.</p>
219         */
220        public final TypeReference<T> getTypeReference() {
221            return mTypeReference;
222        }
223    }
224
225    private static final String TAG = "CameraMetadataJV";
226    private static final boolean DEBUG = false;
227
228    // this should be in sync with HAL_PIXEL_FORMAT_BLOB defined in graphics.h
229    public static final int NATIVE_JPEG_FORMAT = 0x21;
230
231    private static final String CELLID_PROCESS = "CELLID";
232    private static final String GPS_PROCESS = "GPS";
233    private static final int FACE_LANDMARK_SIZE = 6;
234
235    private static String translateLocationProviderToProcess(final String provider) {
236        if (provider == null) {
237            return null;
238        }
239        switch(provider) {
240            case LocationManager.GPS_PROVIDER:
241                return GPS_PROCESS;
242            case LocationManager.NETWORK_PROVIDER:
243                return CELLID_PROCESS;
244            default:
245                return null;
246        }
247    }
248
249    private static String translateProcessToLocationProvider(final String process) {
250        if (process == null) {
251            return null;
252        }
253        switch(process) {
254            case GPS_PROCESS:
255                return LocationManager.GPS_PROVIDER;
256            case CELLID_PROCESS:
257                return LocationManager.NETWORK_PROVIDER;
258            default:
259                return null;
260        }
261    }
262
263    public CameraMetadataNative() {
264        super();
265        mMetadataPtr = nativeAllocate();
266        if (mMetadataPtr == 0) {
267            throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
268        }
269    }
270
271    /**
272     * Copy constructor - clone metadata
273     */
274    public CameraMetadataNative(CameraMetadataNative other) {
275        super();
276        mMetadataPtr = nativeAllocateCopy(other);
277        if (mMetadataPtr == 0) {
278            throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
279        }
280    }
281
282    /**
283     * Move the contents from {@code other} into a new camera metadata instance.</p>
284     *
285     * <p>After this call, {@code other} will become empty.</p>
286     *
287     * @param other the previous metadata instance which will get pilfered
288     * @return a new metadata instance with the values from {@code other} moved into it
289     */
290    public static CameraMetadataNative move(CameraMetadataNative other) {
291        CameraMetadataNative newObject = new CameraMetadataNative();
292        newObject.swap(other);
293        return newObject;
294    }
295
296    public static final Parcelable.Creator<CameraMetadataNative> CREATOR =
297            new Parcelable.Creator<CameraMetadataNative>() {
298        @Override
299        public CameraMetadataNative createFromParcel(Parcel in) {
300            CameraMetadataNative metadata = new CameraMetadataNative();
301            metadata.readFromParcel(in);
302            return metadata;
303        }
304
305        @Override
306        public CameraMetadataNative[] newArray(int size) {
307            return new CameraMetadataNative[size];
308        }
309    };
310
311    @Override
312    public int describeContents() {
313        return 0;
314    }
315
316    @Override
317    public void writeToParcel(Parcel dest, int flags) {
318        nativeWriteToParcel(dest);
319    }
320
321    /**
322     * @hide
323     */
324    public <T> T get(CameraCharacteristics.Key<T> key) {
325        return get(key.getNativeKey());
326    }
327
328    /**
329     * @hide
330     */
331    public <T> T get(CaptureResult.Key<T> key) {
332        return get(key.getNativeKey());
333    }
334
335    /**
336     * @hide
337     */
338    public <T> T get(CaptureRequest.Key<T> key) {
339        return get(key.getNativeKey());
340    }
341
342    /**
343     * Look-up a metadata field value by its key.
344     *
345     * @param key a non-{@code null} key instance
346     * @return the field corresponding to the {@code key}, or {@code null} if no value was set
347     */
348    public <T> T get(Key<T> key) {
349        Preconditions.checkNotNull(key, "key must not be null");
350
351        // Check if key has been overridden to use a wrapper class on the java side.
352        GetCommand g = sGetCommandMap.get(key);
353        if (g != null) {
354            return g.getValue(this, key);
355        }
356        return getBase(key);
357    }
358
359    public void readFromParcel(Parcel in) {
360        nativeReadFromParcel(in);
361    }
362
363    /**
364     * Set the global client-side vendor tag descriptor to allow use of vendor
365     * tags in camera applications.
366     *
367     * @throws ServiceSpecificException
368     * @hide
369     */
370    public static void setupGlobalVendorTagDescriptor() throws ServiceSpecificException {
371        int err = nativeSetupGlobalVendorTagDescriptor();
372        if (err != 0) {
373            throw new ServiceSpecificException(err, "Failure to set up global vendor tags");
374        }
375    }
376
377    /**
378     * Set the global client-side vendor tag descriptor to allow use of vendor
379     * tags in camera applications.
380     *
381     * @return int An error code corresponding to one of the
382     * {@link ICameraService} error constants, or 0 on success.
383     */
384    private static native int nativeSetupGlobalVendorTagDescriptor();
385
386    /**
387     * Set a camera metadata field to a value. The field definitions can be
388     * found in {@link CameraCharacteristics}, {@link CaptureResult}, and
389     * {@link CaptureRequest}.
390     *
391     * @param key The metadata field to write.
392     * @param value The value to set the field to, which must be of a matching
393     * type to the key.
394     */
395    public <T> void set(Key<T> key, T value) {
396        SetCommand s = sSetCommandMap.get(key);
397        if (s != null) {
398            s.setValue(this, value);
399            return;
400        }
401
402        setBase(key, value);
403    }
404
405    public <T> void set(CaptureRequest.Key<T> key, T value) {
406        set(key.getNativeKey(), value);
407    }
408
409    public <T> void set(CaptureResult.Key<T> key, T value) {
410        set(key.getNativeKey(), value);
411    }
412
413    public <T> void set(CameraCharacteristics.Key<T> key, T value) {
414        set(key.getNativeKey(), value);
415    }
416
417    // Keep up-to-date with camera_metadata.h
418    /**
419     * @hide
420     */
421    public static final int TYPE_BYTE = 0;
422    /**
423     * @hide
424     */
425    public static final int TYPE_INT32 = 1;
426    /**
427     * @hide
428     */
429    public static final int TYPE_FLOAT = 2;
430    /**
431     * @hide
432     */
433    public static final int TYPE_INT64 = 3;
434    /**
435     * @hide
436     */
437    public static final int TYPE_DOUBLE = 4;
438    /**
439     * @hide
440     */
441    public static final int TYPE_RATIONAL = 5;
442    /**
443     * @hide
444     */
445    public static final int NUM_TYPES = 6;
446
447    private void close() {
448        // this sets mMetadataPtr to 0
449        nativeClose();
450        mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final
451    }
452
453    private <T> T getBase(CameraCharacteristics.Key<T> key) {
454        return getBase(key.getNativeKey());
455    }
456
457    private <T> T getBase(CaptureResult.Key<T> key) {
458        return getBase(key.getNativeKey());
459    }
460
461    private <T> T getBase(CaptureRequest.Key<T> key) {
462        return getBase(key.getNativeKey());
463    }
464
465    private <T> T getBase(Key<T> key) {
466        int tag = key.getTag();
467        byte[] values = readValues(tag);
468        if (values == null) {
469            return null;
470        }
471
472        Marshaler<T> marshaler = getMarshalerForKey(key);
473        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
474        return marshaler.unmarshal(buffer);
475    }
476
477    // Use Command pattern here to avoid lots of expensive if/equals checks in get for overridden
478    // metadata.
479    private static final HashMap<Key<?>, GetCommand> sGetCommandMap =
480            new HashMap<Key<?>, GetCommand>();
481    static {
482        sGetCommandMap.put(
483                CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey(), new GetCommand() {
484                    @Override
485                    @SuppressWarnings("unchecked")
486                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
487                        return (T) metadata.getAvailableFormats();
488                    }
489                });
490        sGetCommandMap.put(
491                CaptureResult.STATISTICS_FACES.getNativeKey(), new GetCommand() {
492                    @Override
493                    @SuppressWarnings("unchecked")
494                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
495                        return (T) metadata.getFaces();
496                    }
497                });
498        sGetCommandMap.put(
499                CaptureResult.STATISTICS_FACE_RECTANGLES.getNativeKey(), new GetCommand() {
500                    @Override
501                    @SuppressWarnings("unchecked")
502                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
503                        return (T) metadata.getFaceRectangles();
504                    }
505                });
506        sGetCommandMap.put(
507                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP.getNativeKey(),
508                        new GetCommand() {
509                    @Override
510                    @SuppressWarnings("unchecked")
511                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
512                        return (T) metadata.getStreamConfigurationMap();
513                    }
514                });
515        sGetCommandMap.put(
516                CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() {
517                    @Override
518                    @SuppressWarnings("unchecked")
519                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
520                        return (T) metadata.getMaxRegions(key);
521                    }
522                });
523        sGetCommandMap.put(
524                CameraCharacteristics.CONTROL_MAX_REGIONS_AWB.getNativeKey(), new GetCommand() {
525                    @Override
526                    @SuppressWarnings("unchecked")
527                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
528                        return (T) metadata.getMaxRegions(key);
529                    }
530                });
531        sGetCommandMap.put(
532                CameraCharacteristics.CONTROL_MAX_REGIONS_AF.getNativeKey(), new GetCommand() {
533                    @Override
534                    @SuppressWarnings("unchecked")
535                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
536                        return (T) metadata.getMaxRegions(key);
537                    }
538                });
539        sGetCommandMap.put(
540                CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW.getNativeKey(), new GetCommand() {
541                    @Override
542                    @SuppressWarnings("unchecked")
543                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
544                        return (T) metadata.getMaxNumOutputs(key);
545                    }
546                });
547        sGetCommandMap.put(
548                CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC.getNativeKey(), new GetCommand() {
549                    @Override
550                    @SuppressWarnings("unchecked")
551                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
552                        return (T) metadata.getMaxNumOutputs(key);
553                    }
554                });
555        sGetCommandMap.put(
556                CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING.getNativeKey(),
557                        new GetCommand() {
558                    @Override
559                    @SuppressWarnings("unchecked")
560                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
561                        return (T) metadata.getMaxNumOutputs(key);
562                    }
563                });
564        sGetCommandMap.put(
565                CaptureRequest.TONEMAP_CURVE.getNativeKey(), new GetCommand() {
566                    @Override
567                    @SuppressWarnings("unchecked")
568                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
569                        return (T) metadata.getTonemapCurve();
570                    }
571                });
572        sGetCommandMap.put(
573                CaptureResult.JPEG_GPS_LOCATION.getNativeKey(), new GetCommand() {
574                    @Override
575                    @SuppressWarnings("unchecked")
576                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
577                        return (T) metadata.getGpsLocation();
578                    }
579                });
580        sGetCommandMap.put(
581                CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP.getNativeKey(),
582                        new GetCommand() {
583                    @Override
584                    @SuppressWarnings("unchecked")
585                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
586                        return (T) metadata.getLensShadingMap();
587                    }
588                });
589    }
590
591    private int[] getAvailableFormats() {
592        int[] availableFormats = getBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS);
593        if (availableFormats != null) {
594            for (int i = 0; i < availableFormats.length; i++) {
595                // JPEG has different value between native and managed side, need override.
596                if (availableFormats[i] == NATIVE_JPEG_FORMAT) {
597                    availableFormats[i] = ImageFormat.JPEG;
598                }
599            }
600        }
601
602        return availableFormats;
603    }
604
605    private boolean setFaces(Face[] faces) {
606        if (faces == null) {
607            return false;
608        }
609
610        int numFaces = faces.length;
611
612        // Detect if all faces are SIMPLE or not; count # of valid faces
613        boolean fullMode = true;
614        for (Face face : faces) {
615            if (face == null) {
616                numFaces--;
617                Log.w(TAG, "setFaces - null face detected, skipping");
618                continue;
619            }
620
621            if (face.getId() == Face.ID_UNSUPPORTED) {
622                fullMode = false;
623            }
624        }
625
626        Rect[] faceRectangles = new Rect[numFaces];
627        byte[] faceScores = new byte[numFaces];
628        int[] faceIds = null;
629        int[] faceLandmarks = null;
630
631        if (fullMode) {
632            faceIds = new int[numFaces];
633            faceLandmarks = new int[numFaces * FACE_LANDMARK_SIZE];
634        }
635
636        int i = 0;
637        for (Face face : faces) {
638            if (face == null) {
639                continue;
640            }
641
642            faceRectangles[i] = face.getBounds();
643            faceScores[i] = (byte)face.getScore();
644
645            if (fullMode) {
646                faceIds[i] = face.getId();
647
648                int j = 0;
649
650                faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getLeftEyePosition().x;
651                faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getLeftEyePosition().y;
652                faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getRightEyePosition().x;
653                faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getRightEyePosition().y;
654                faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getMouthPosition().x;
655                faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getMouthPosition().y;
656            }
657
658            i++;
659        }
660
661        set(CaptureResult.STATISTICS_FACE_RECTANGLES, faceRectangles);
662        set(CaptureResult.STATISTICS_FACE_IDS, faceIds);
663        set(CaptureResult.STATISTICS_FACE_LANDMARKS, faceLandmarks);
664        set(CaptureResult.STATISTICS_FACE_SCORES, faceScores);
665
666        return true;
667    }
668
669    private Face[] getFaces() {
670        Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE);
671        byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES);
672        Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES);
673        int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS);
674        int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS);
675
676        if (areValuesAllNull(faceDetectMode, faceScores, faceRectangles, faceIds, faceLandmarks)) {
677            return null;
678        }
679
680        if (faceDetectMode == null) {
681            Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE");
682            faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE;
683        } else {
684            if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) {
685                return new Face[0];
686            }
687            if (faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE &&
688                    faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
689                Log.w(TAG, "Unknown face detect mode: " + faceDetectMode);
690                return new Face[0];
691            }
692        }
693
694        // Face scores and rectangles are required by SIMPLE and FULL mode.
695        if (faceScores == null || faceRectangles == null) {
696            Log.w(TAG, "Expect face scores and rectangles to be non-null");
697            return new Face[0];
698        } else if (faceScores.length != faceRectangles.length) {
699            Log.w(TAG, String.format("Face score size(%d) doesn match face rectangle size(%d)!",
700                    faceScores.length, faceRectangles.length));
701        }
702
703        // To be safe, make number of faces is the minimal of all face info metadata length.
704        int numFaces = Math.min(faceScores.length, faceRectangles.length);
705        // Face id and landmarks are only required by FULL mode.
706        if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
707            if (faceIds == null || faceLandmarks == null) {
708                Log.w(TAG, "Expect face ids and landmarks to be non-null for FULL mode," +
709                        "fallback to SIMPLE mode");
710                faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE;
711            } else {
712                if (faceIds.length != numFaces ||
713                        faceLandmarks.length != numFaces * FACE_LANDMARK_SIZE) {
714                    Log.w(TAG, String.format("Face id size(%d), or face landmark size(%d) don't" +
715                            "match face number(%d)!",
716                            faceIds.length, faceLandmarks.length * FACE_LANDMARK_SIZE, numFaces));
717                }
718                // To be safe, make number of faces is the minimal of all face info metadata length.
719                numFaces = Math.min(numFaces, faceIds.length);
720                numFaces = Math.min(numFaces, faceLandmarks.length / FACE_LANDMARK_SIZE);
721            }
722        }
723
724        ArrayList<Face> faceList = new ArrayList<Face>();
725        if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE) {
726            for (int i = 0; i < numFaces; i++) {
727                if (faceScores[i] <= Face.SCORE_MAX &&
728                        faceScores[i] >= Face.SCORE_MIN) {
729                    faceList.add(new Face(faceRectangles[i], faceScores[i]));
730                }
731            }
732        } else {
733            // CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL
734            for (int i = 0; i < numFaces; i++) {
735                if (faceScores[i] <= Face.SCORE_MAX &&
736                        faceScores[i] >= Face.SCORE_MIN &&
737                        faceIds[i] >= 0) {
738                    Point leftEye = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE],
739                            faceLandmarks[i*FACE_LANDMARK_SIZE+1]);
740                    Point rightEye = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE+2],
741                            faceLandmarks[i*FACE_LANDMARK_SIZE+3]);
742                    Point mouth = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE+4],
743                            faceLandmarks[i*FACE_LANDMARK_SIZE+5]);
744                    Face face = new Face(faceRectangles[i], faceScores[i], faceIds[i],
745                            leftEye, rightEye, mouth);
746                    faceList.add(face);
747                }
748            }
749        }
750        Face[] faces = new Face[faceList.size()];
751        faceList.toArray(faces);
752        return faces;
753    }
754
755    // Face rectangles are defined as (left, top, right, bottom) instead of
756    // (left, top, width, height) at the native level, so the normal Rect
757    // conversion that does (l, t, w, h) -> (l, t, r, b) is unnecessary. Undo
758    // that conversion here for just the faces.
759    private Rect[] getFaceRectangles() {
760        Rect[] faceRectangles = getBase(CaptureResult.STATISTICS_FACE_RECTANGLES);
761        if (faceRectangles == null) return null;
762
763        Rect[] fixedFaceRectangles = new Rect[faceRectangles.length];
764        for (int i = 0; i < faceRectangles.length; i++) {
765            fixedFaceRectangles[i] = new Rect(
766                    faceRectangles[i].left,
767                    faceRectangles[i].top,
768                    faceRectangles[i].right - faceRectangles[i].left,
769                    faceRectangles[i].bottom - faceRectangles[i].top);
770        }
771        return fixedFaceRectangles;
772    }
773
774    private LensShadingMap getLensShadingMap() {
775        float[] lsmArray = getBase(CaptureResult.STATISTICS_LENS_SHADING_MAP);
776        Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE);
777
778        // Do not warn if lsmArray is null while s is not. This is valid.
779        if (lsmArray == null) {
780            return null;
781        }
782
783        if (s == null) {
784            Log.w(TAG, "getLensShadingMap - Lens shading map size was null.");
785            return null;
786        }
787
788        LensShadingMap map = new LensShadingMap(lsmArray, s.getHeight(), s.getWidth());
789        return map;
790    }
791
792    private Location getGpsLocation() {
793        String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
794        double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
795        Long timeStamp = get(CaptureResult.JPEG_GPS_TIMESTAMP);
796
797        if (areValuesAllNull(processingMethod, coords, timeStamp)) {
798            return null;
799        }
800
801        Location l = new Location(translateProcessToLocationProvider(processingMethod));
802        if (timeStamp != null) {
803            l.setTime(timeStamp);
804        } else {
805            Log.w(TAG, "getGpsLocation - No timestamp for GPS location.");
806        }
807
808        if (coords != null) {
809            l.setLatitude(coords[0]);
810            l.setLongitude(coords[1]);
811            l.setAltitude(coords[2]);
812        } else {
813            Log.w(TAG, "getGpsLocation - No coordinates for GPS location");
814        }
815
816        return l;
817    }
818
819    private boolean setGpsLocation(Location l) {
820        if (l == null) {
821            return false;
822        }
823
824        double[] coords = { l.getLatitude(), l.getLongitude(), l.getAltitude() };
825        String processMethod = translateLocationProviderToProcess(l.getProvider());
826        long timestamp = l.getTime();
827
828        set(CaptureRequest.JPEG_GPS_TIMESTAMP, timestamp);
829        set(CaptureRequest.JPEG_GPS_COORDINATES, coords);
830
831        if (processMethod == null) {
832            Log.w(TAG, "setGpsLocation - No process method, Location is not from a GPS or NETWORK" +
833                    "provider");
834        } else {
835            setBase(CaptureRequest.JPEG_GPS_PROCESSING_METHOD, processMethod);
836        }
837        return true;
838    }
839
840    private StreamConfigurationMap getStreamConfigurationMap() {
841        StreamConfiguration[] configurations = getBase(
842                CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
843        StreamConfigurationDuration[] minFrameDurations = getBase(
844                CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
845        StreamConfigurationDuration[] stallDurations = getBase(
846                CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS);
847        StreamConfiguration[] depthConfigurations = getBase(
848                CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS);
849        StreamConfigurationDuration[] depthMinFrameDurations = getBase(
850                CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS);
851        StreamConfigurationDuration[] depthStallDurations = getBase(
852                CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS);
853        HighSpeedVideoConfiguration[] highSpeedVideoConfigurations = getBase(
854                CameraCharacteristics.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS);
855        ReprocessFormatsMap inputOutputFormatsMap = getBase(
856                CameraCharacteristics.SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP);
857        int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
858        boolean listHighResolution = false;
859        for (int capability : capabilities) {
860            if (capability == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE) {
861                listHighResolution = true;
862                break;
863            }
864        }
865        return new StreamConfigurationMap(
866                configurations, minFrameDurations, stallDurations,
867                depthConfigurations, depthMinFrameDurations, depthStallDurations,
868                highSpeedVideoConfigurations, inputOutputFormatsMap,
869                listHighResolution);
870    }
871
872    private <T> Integer getMaxRegions(Key<T> key) {
873        final int AE = 0;
874        final int AWB = 1;
875        final int AF = 2;
876
877        // The order of the elements is: (AE, AWB, AF)
878        int[] maxRegions = getBase(CameraCharacteristics.CONTROL_MAX_REGIONS);
879
880        if (maxRegions == null) {
881            return null;
882        }
883
884        if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AE)) {
885            return maxRegions[AE];
886        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB)) {
887            return maxRegions[AWB];
888        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AF)) {
889            return maxRegions[AF];
890        } else {
891            throw new AssertionError("Invalid key " + key);
892        }
893    }
894
895    private <T> Integer getMaxNumOutputs(Key<T> key) {
896        final int RAW = 0;
897        final int PROC = 1;
898        final int PROC_STALLING = 2;
899
900        // The order of the elements is: (raw, proc+nonstalling, proc+stalling)
901        int[] maxNumOutputs = getBase(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS);
902
903        if (maxNumOutputs == null) {
904            return null;
905        }
906
907        if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW)) {
908            return maxNumOutputs[RAW];
909        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC)) {
910            return maxNumOutputs[PROC];
911        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING)) {
912            return maxNumOutputs[PROC_STALLING];
913        } else {
914            throw new AssertionError("Invalid key " + key);
915        }
916    }
917
918    private <T> TonemapCurve getTonemapCurve() {
919        float[] red = getBase(CaptureRequest.TONEMAP_CURVE_RED);
920        float[] green = getBase(CaptureRequest.TONEMAP_CURVE_GREEN);
921        float[] blue = getBase(CaptureRequest.TONEMAP_CURVE_BLUE);
922
923        if (areValuesAllNull(red, green, blue)) {
924            return null;
925        }
926
927        if (red == null || green == null || blue == null) {
928            Log.w(TAG, "getTonemapCurve - missing tone curve components");
929            return null;
930        }
931        TonemapCurve tc = new TonemapCurve(red, green, blue);
932        return tc;
933    }
934
935    private <T> void setBase(CameraCharacteristics.Key<T> key, T value) {
936        setBase(key.getNativeKey(), value);
937    }
938
939    private <T> void setBase(CaptureResult.Key<T> key, T value) {
940        setBase(key.getNativeKey(), value);
941    }
942
943    private <T> void setBase(CaptureRequest.Key<T> key, T value) {
944        setBase(key.getNativeKey(), value);
945    }
946
947    private <T> void setBase(Key<T> key, T value) {
948        int tag = key.getTag();
949
950        if (value == null) {
951            // Erase the entry
952            writeValues(tag, /*src*/null);
953            return;
954        } // else update the entry to a new value
955
956        Marshaler<T> marshaler = getMarshalerForKey(key);
957        int size = marshaler.calculateMarshalSize(value);
958
959        // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
960        byte[] values = new byte[size];
961
962        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
963        marshaler.marshal(value, buffer);
964
965        writeValues(tag, values);
966    }
967
968    // Use Command pattern here to avoid lots of expensive if/equals checks in get for overridden
969    // metadata.
970    private static final HashMap<Key<?>, SetCommand> sSetCommandMap =
971            new HashMap<Key<?>, SetCommand>();
972    static {
973        sSetCommandMap.put(CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey(),
974                new SetCommand() {
975            @Override
976            public <T> void setValue(CameraMetadataNative metadata, T value) {
977                metadata.setAvailableFormats((int[]) value);
978            }
979        });
980        sSetCommandMap.put(CaptureResult.STATISTICS_FACE_RECTANGLES.getNativeKey(),
981                new SetCommand() {
982            @Override
983            public <T> void setValue(CameraMetadataNative metadata, T value) {
984                metadata.setFaceRectangles((Rect[]) value);
985            }
986        });
987        sSetCommandMap.put(CaptureResult.STATISTICS_FACES.getNativeKey(),
988                new SetCommand() {
989            @Override
990            public <T> void setValue(CameraMetadataNative metadata, T value) {
991                metadata.setFaces((Face[])value);
992            }
993        });
994        sSetCommandMap.put(CaptureRequest.TONEMAP_CURVE.getNativeKey(), new SetCommand() {
995            @Override
996            public <T> void setValue(CameraMetadataNative metadata, T value) {
997                metadata.setTonemapCurve((TonemapCurve) value);
998            }
999        });
1000        sSetCommandMap.put(CaptureResult.JPEG_GPS_LOCATION.getNativeKey(), new SetCommand() {
1001            @Override
1002            public <T> void setValue(CameraMetadataNative metadata, T value) {
1003                metadata.setGpsLocation((Location) value);
1004            }
1005        });
1006    }
1007
1008    private boolean setAvailableFormats(int[] value) {
1009        int[] availableFormat = value;
1010        if (value == null) {
1011            // Let setBase() to handle the null value case.
1012            return false;
1013        }
1014
1015        int[] newValues = new int[availableFormat.length];
1016        for (int i = 0; i < availableFormat.length; i++) {
1017            newValues[i] = availableFormat[i];
1018            if (availableFormat[i] == ImageFormat.JPEG) {
1019                newValues[i] = NATIVE_JPEG_FORMAT;
1020            }
1021        }
1022
1023        setBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS, newValues);
1024        return true;
1025    }
1026
1027    /**
1028     * Convert Face Rectangles from managed side to native side as they have different definitions.
1029     * <p>
1030     * Managed side face rectangles are defined as: left, top, width, height.
1031     * Native side face rectangles are defined as: left, top, right, bottom.
1032     * The input face rectangle need to be converted to native side definition when set is called.
1033     * </p>
1034     *
1035     * @param faceRects Input face rectangles.
1036     * @return true if face rectangles can be set successfully. Otherwise, Let the caller
1037     *             (setBase) to handle it appropriately.
1038     */
1039    private boolean setFaceRectangles(Rect[] faceRects) {
1040        if (faceRects == null) {
1041            return false;
1042        }
1043
1044        Rect[] newFaceRects = new Rect[faceRects.length];
1045        for (int i = 0; i < newFaceRects.length; i++) {
1046            newFaceRects[i] = new Rect(
1047                    faceRects[i].left,
1048                    faceRects[i].top,
1049                    faceRects[i].right + faceRects[i].left,
1050                    faceRects[i].bottom + faceRects[i].top);
1051        }
1052
1053        setBase(CaptureResult.STATISTICS_FACE_RECTANGLES, newFaceRects);
1054        return true;
1055    }
1056
1057    private <T> boolean setTonemapCurve(TonemapCurve tc) {
1058        if (tc == null) {
1059            return false;
1060        }
1061
1062        float[][] curve = new float[3][];
1063        for (int i = TonemapCurve.CHANNEL_RED; i <= TonemapCurve.CHANNEL_BLUE; i++) {
1064            int pointCount = tc.getPointCount(i);
1065            curve[i] = new float[pointCount * TonemapCurve.POINT_SIZE];
1066            tc.copyColorCurve(i, curve[i], 0);
1067        }
1068        setBase(CaptureRequest.TONEMAP_CURVE_RED, curve[0]);
1069        setBase(CaptureRequest.TONEMAP_CURVE_GREEN, curve[1]);
1070        setBase(CaptureRequest.TONEMAP_CURVE_BLUE, curve[2]);
1071
1072        return true;
1073    }
1074
1075    private long mMetadataPtr; // native CameraMetadata*
1076
1077    private native long nativeAllocate();
1078    private native long nativeAllocateCopy(CameraMetadataNative other)
1079            throws NullPointerException;
1080
1081    private native synchronized void nativeWriteToParcel(Parcel dest);
1082    private native synchronized void nativeReadFromParcel(Parcel source);
1083    private native synchronized void nativeSwap(CameraMetadataNative other)
1084            throws NullPointerException;
1085    private native synchronized void nativeClose();
1086    private native synchronized boolean nativeIsEmpty();
1087    private native synchronized int nativeGetEntryCount();
1088
1089    private native synchronized byte[] nativeReadValues(int tag);
1090    private native synchronized void nativeWriteValues(int tag, byte[] src);
1091    private native synchronized void nativeDump() throws IOException; // dump to ALOGD
1092
1093    private static native ArrayList nativeGetAllVendorKeys(Class keyClass);
1094    private static native int nativeGetTagFromKey(String keyName)
1095            throws IllegalArgumentException;
1096    private static native int nativeGetTypeFromTag(int tag)
1097            throws IllegalArgumentException;
1098    private static native void nativeClassInit();
1099
1100    /**
1101     * <p>Perform a 0-copy swap of the internal metadata with another object.</p>
1102     *
1103     * <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p>
1104     *
1105     * @param other Metadata to swap with
1106     * @throws NullPointerException if other was null
1107     * @hide
1108     */
1109    public void swap(CameraMetadataNative other) {
1110        nativeSwap(other);
1111    }
1112
1113    /**
1114     * @hide
1115     */
1116    public int getEntryCount() {
1117        return nativeGetEntryCount();
1118    }
1119
1120    /**
1121     * Does this metadata contain at least 1 entry?
1122     *
1123     * @hide
1124     */
1125    public boolean isEmpty() {
1126        return nativeIsEmpty();
1127    }
1128
1129
1130    /**
1131     * Return a list containing keys of the given key class for all defined vendor tags.
1132     *
1133     * @hide
1134     */
1135    public static <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) {
1136        if (keyClass == null) {
1137            throw new NullPointerException();
1138        }
1139        return (ArrayList<K>) nativeGetAllVendorKeys(keyClass);
1140    }
1141
1142    /**
1143     * Convert a key string into the equivalent native tag.
1144     *
1145     * @throws IllegalArgumentException if the key was not recognized
1146     * @throws NullPointerException if the key was null
1147     *
1148     * @hide
1149     */
1150    public static int getTag(String key) {
1151        return nativeGetTagFromKey(key);
1152    }
1153
1154    /**
1155     * Get the underlying native type for a tag.
1156     *
1157     * @param tag An integer tag, see e.g. {@link #getTag}
1158     * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE}
1159     *
1160     * @hide
1161     */
1162    public static int getNativeType(int tag) {
1163        return nativeGetTypeFromTag(tag);
1164    }
1165
1166    /**
1167     * <p>Updates the existing entry for tag with the new bytes pointed by src, erasing
1168     * the entry if src was null.</p>
1169     *
1170     * <p>An empty array can be passed in to update the entry to 0 elements.</p>
1171     *
1172     * @param tag An integer tag, see e.g. {@link #getTag}
1173     * @param src An array of bytes, or null to erase the entry
1174     *
1175     * @hide
1176     */
1177    public void writeValues(int tag, byte[] src) {
1178        nativeWriteValues(tag, src);
1179    }
1180
1181    /**
1182     * <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize
1183     * the data properly.</p>
1184     *
1185     * <p>An empty array can be returned to denote an existing entry with 0 elements.</p>
1186     *
1187     * @param tag An integer tag, see e.g. {@link #getTag}
1188     *
1189     * @return {@code null} if there were 0 entries for this tag, a byte[] otherwise.
1190     * @hide
1191     */
1192    public byte[] readValues(int tag) {
1193        // TODO: Optimization. Native code returns a ByteBuffer instead.
1194        return nativeReadValues(tag);
1195    }
1196
1197    /**
1198     * Dumps the native metadata contents to logcat.
1199     *
1200     * <p>Visibility for testing/debugging only. The results will not
1201     * include any synthesized keys, as they are invisible to the native layer.</p>
1202     *
1203     * @hide
1204     */
1205    public void dumpToLog() {
1206        try {
1207            nativeDump();
1208        } catch (IOException e) {
1209            Log.wtf(TAG, "Dump logging failed", e);
1210        }
1211    }
1212
1213    @Override
1214    protected void finalize() throws Throwable {
1215        try {
1216            close();
1217        } finally {
1218            super.finalize();
1219        }
1220    }
1221
1222    /**
1223     * Get the marshaler compatible with the {@code key} and type {@code T}.
1224     *
1225     * @throws UnsupportedOperationException
1226     *          if the native/managed type combination for {@code key} is not supported
1227     */
1228    private static <T> Marshaler<T> getMarshalerForKey(Key<T> key) {
1229        return MarshalRegistry.getMarshaler(key.getTypeReference(),
1230                getNativeType(key.getTag()));
1231    }
1232
1233    @SuppressWarnings({ "unchecked", "rawtypes" })
1234    private static void registerAllMarshalers() {
1235        if (DEBUG) {
1236            Log.v(TAG, "Shall register metadata marshalers");
1237        }
1238
1239        MarshalQueryable[] queryList = new MarshalQueryable[] {
1240                // marshalers for standard types
1241                new MarshalQueryablePrimitive(),
1242                new MarshalQueryableEnum(),
1243                new MarshalQueryableArray(),
1244
1245                // pseudo standard types, that expand/narrow the native type into a managed type
1246                new MarshalQueryableBoolean(),
1247                new MarshalQueryableNativeByteToInteger(),
1248
1249                // marshalers for custom types
1250                new MarshalQueryableRect(),
1251                new MarshalQueryableSize(),
1252                new MarshalQueryableSizeF(),
1253                new MarshalQueryableString(),
1254                new MarshalQueryableReprocessFormatsMap(),
1255                new MarshalQueryableRange(),
1256                new MarshalQueryablePair(),
1257                new MarshalQueryableMeteringRectangle(),
1258                new MarshalQueryableColorSpaceTransform(),
1259                new MarshalQueryableStreamConfiguration(),
1260                new MarshalQueryableStreamConfigurationDuration(),
1261                new MarshalQueryableRggbChannelVector(),
1262                new MarshalQueryableBlackLevelPattern(),
1263                new MarshalQueryableHighSpeedVideoConfiguration(),
1264
1265                // generic parcelable marshaler (MUST BE LAST since it has lowest priority)
1266                new MarshalQueryableParcelable(),
1267        };
1268
1269        for (MarshalQueryable query : queryList) {
1270            MarshalRegistry.registerMarshalQueryable(query);
1271        }
1272        if (DEBUG) {
1273            Log.v(TAG, "Registered metadata marshalers");
1274        }
1275    }
1276
1277    /** Check if input arguments are all {@code null}.
1278     *
1279     * @param objs Input arguments for null check
1280     * @return {@code true} if input arguments are all {@code null}, otherwise {@code false}
1281     */
1282    private static boolean areValuesAllNull(Object... objs) {
1283        for (Object o : objs) {
1284            if (o != null) return false;
1285        }
1286        return true;
1287    }
1288
1289    static {
1290        /*
1291         * We use a class initializer to allow the native code to cache some field offsets
1292         */
1293        nativeClassInit();
1294        registerAllMarshalers();
1295    }
1296}
1297