Home | History | Annotate | Download | only in impl
      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 
     17 package android.hardware.camera2.impl;
     18 
     19 import android.graphics.ImageFormat;
     20 import android.graphics.Point;
     21 import android.graphics.Rect;
     22 import android.hardware.camera2.CameraCharacteristics;
     23 import android.hardware.camera2.CaptureRequest;
     24 import android.hardware.camera2.CaptureResult;
     25 import android.hardware.camera2.marshal.Marshaler;
     26 import android.hardware.camera2.marshal.MarshalQueryable;
     27 import android.hardware.camera2.marshal.MarshalRegistry;
     28 import android.hardware.camera2.marshal.impl.MarshalQueryableArray;
     29 import android.hardware.camera2.marshal.impl.MarshalQueryableBoolean;
     30 import android.hardware.camera2.marshal.impl.MarshalQueryableBlackLevelPattern;
     31 import android.hardware.camera2.marshal.impl.MarshalQueryableColorSpaceTransform;
     32 import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
     33 import android.hardware.camera2.marshal.impl.MarshalQueryableHighSpeedVideoConfiguration;
     34 import android.hardware.camera2.marshal.impl.MarshalQueryableMeteringRectangle;
     35 import android.hardware.camera2.marshal.impl.MarshalQueryableNativeByteToInteger;
     36 import android.hardware.camera2.marshal.impl.MarshalQueryablePair;
     37 import android.hardware.camera2.marshal.impl.MarshalQueryableParcelable;
     38 import android.hardware.camera2.marshal.impl.MarshalQueryablePrimitive;
     39 import android.hardware.camera2.marshal.impl.MarshalQueryableRange;
     40 import android.hardware.camera2.marshal.impl.MarshalQueryableRect;
     41 import android.hardware.camera2.marshal.impl.MarshalQueryableReprocessFormatsMap;
     42 import android.hardware.camera2.marshal.impl.MarshalQueryableRggbChannelVector;
     43 import android.hardware.camera2.marshal.impl.MarshalQueryableSize;
     44 import android.hardware.camera2.marshal.impl.MarshalQueryableSizeF;
     45 import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfiguration;
     46 import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration;
     47 import android.hardware.camera2.marshal.impl.MarshalQueryableString;
     48 import android.hardware.camera2.params.Face;
     49 import android.hardware.camera2.params.HighSpeedVideoConfiguration;
     50 import android.hardware.camera2.params.LensShadingMap;
     51 import android.hardware.camera2.params.ReprocessFormatsMap;
     52 import android.hardware.camera2.params.StreamConfiguration;
     53 import android.hardware.camera2.params.StreamConfigurationDuration;
     54 import android.hardware.camera2.params.StreamConfigurationMap;
     55 import android.hardware.camera2.params.TonemapCurve;
     56 import android.hardware.camera2.utils.TypeReference;
     57 import android.location.Location;
     58 import android.location.LocationManager;
     59 import android.os.Parcelable;
     60 import android.os.Parcel;
     61 import android.os.ServiceSpecificException;
     62 import android.util.Log;
     63 import android.util.Size;
     64 
     65 import com.android.internal.util.Preconditions;
     66 
     67 import java.io.IOException;
     68 import java.nio.ByteBuffer;
     69 import java.nio.ByteOrder;
     70 import java.util.ArrayList;
     71 import java.util.HashMap;
     72 
     73 /**
     74  * Implementation of camera metadata marshal/unmarshal across Binder to
     75  * the camera service
     76  */
     77 public 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