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