Camera: Initial support for vendor tag caches

Vendor tag cache will be used alternatively to the regular
Vendor tag descriptor. The caches can support multiple vendor
tag providers at the same time. The native metadata along with
the requests/results/characteristics will store vendor specific
information that will be used to indentify the respective
descriptor.

Bug: 34275821
Test: Complete Camera/Camera2 CTS tests
Change-Id: I50b7cf9aa5575944fde7673a1728869690b2ce0d
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 2aa6af6..46ad3f0 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -67,6 +67,15 @@
          *
          * @hide
          */
+        public Key(String name, Class<T> type, long vendorId) {
+            mKey = new CameraMetadataNative.Key<T>(name,  type, vendorId);
+        }
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
         public Key(String name, Class<T> type) {
             mKey = new CameraMetadataNative.Key<T>(name,  type);
         }
@@ -99,6 +108,15 @@
         }
 
         /**
+         * Return vendor tag id.
+         *
+         * @hide
+         */
+        public long getVendorId() {
+            return mKey.getVendorId();
+        }
+
+        /**
          * {@inheritDoc}
          */
         @Override
@@ -159,6 +177,7 @@
      */
     public CameraCharacteristics(CameraMetadataNative properties) {
         mProperties = CameraMetadataNative.move(properties);
+        setNativeInstance(mProperties);
     }
 
     /**
@@ -227,7 +246,7 @@
         }
 
         mKeys = Collections.unmodifiableList(
-                getKeysStatic(getClass(), getKeyClass(), this, filterTags));
+                getKeys(getClass(), getKeyClass(), this, filterTags));
         return mKeys;
     }
 
@@ -320,7 +339,7 @@
                     "metadataClass must be a subclass of CameraMetadata");
         }
 
-        List<TKey> staticKeyList = CameraCharacteristics.<TKey>getKeysStatic(
+        List<TKey> staticKeyList = getKeys(
                 metadataClass, keyClass, /*instance*/null, filterTags);
         return Collections.unmodifiableList(staticKeyList);
     }
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index e289627..8c8c49f 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -52,6 +52,7 @@
 
     private static final String TAG = "CameraMetadataAb";
     private static final boolean DEBUG = false;
+    private CameraMetadataNative mNativeInstance = null;
 
     /**
      * Set a camera metadata field to a value. The field definitions can be
@@ -89,6 +90,13 @@
      /**
       * @hide
       */
+     protected void setNativeInstance(CameraMetadataNative nativeInstance) {
+        mNativeInstance = nativeInstance;
+     }
+
+     /**
+      * @hide
+      */
      protected abstract Class<TKey> getKeyClass();
 
     /**
@@ -108,7 +116,7 @@
     public List<TKey> getKeys() {
         Class<CameraMetadata<TKey>> thisClass = (Class<CameraMetadata<TKey>>) getClass();
         return Collections.unmodifiableList(
-                getKeysStatic(thisClass, getKeyClass(), this, /*filterTags*/null));
+                getKeys(thisClass, getKeyClass(), this, /*filterTags*/null));
     }
 
     /**
@@ -126,7 +134,7 @@
      * </p>
      */
      /*package*/ @SuppressWarnings("unchecked")
-    static <TKey> ArrayList<TKey> getKeysStatic(
+    <TKey> ArrayList<TKey> getKeys(
              Class<?> type, Class<TKey> keyClass,
              CameraMetadata<TKey> instance,
              int[] filterTags) {
@@ -173,23 +181,31 @@
             }
         }
 
-        ArrayList<TKey> vendorKeys = CameraMetadataNative.getAllVendorKeys(keyClass);
+        if (null == mNativeInstance) {
+            return keyList;
+        }
+
+        ArrayList<TKey> vendorKeys = mNativeInstance.getAllVendorKeys(keyClass);
 
         if (vendorKeys != null) {
             for (TKey k : vendorKeys) {
                 String keyName;
+                long vendorId;
                 if (k instanceof CaptureRequest.Key<?>) {
                     keyName = ((CaptureRequest.Key<?>) k).getName();
+                    vendorId = ((CaptureRequest.Key<?>) k).getVendorId();
                 } else if (k instanceof CaptureResult.Key<?>) {
                     keyName = ((CaptureResult.Key<?>) k).getName();
+                    vendorId = ((CaptureResult.Key<?>) k).getVendorId();
                 } else if (k instanceof CameraCharacteristics.Key<?>) {
                     keyName = ((CameraCharacteristics.Key<?>) k).getName();
+                    vendorId = ((CameraCharacteristics.Key<?>) k).getVendorId();
                 } else {
                     continue;
                 }
 
                 if (filterTags == null || Arrays.binarySearch(filterTags,
-                        CameraMetadataNative.getTag(keyName)) >= 0) {
+                        CameraMetadataNative.getTag(keyName, vendorId)) >= 0) {
                     keyList.add(k);
                 }
             }
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 12b46c1..1cf8f03 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -101,6 +101,15 @@
          *
          * @hide
          */
+        public Key(String name, Class<T> type, long vendorId) {
+            mKey = new CameraMetadataNative.Key<T>(name, type, vendorId);
+        }
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
         public Key(String name, Class<T> type) {
             mKey = new CameraMetadataNative.Key<T>(name, type);
         }
@@ -133,6 +142,15 @@
         }
 
         /**
+         * Return vendor tag id.
+         *
+         * @hide
+         */
+        public long getVendorId() {
+            return mKey.getVendorId();
+        }
+
+        /**
          * {@inheritDoc}
          */
         @Override
@@ -199,6 +217,7 @@
      */
     private CaptureRequest() {
         mSettings = new CameraMetadataNative();
+        setNativeInstance(mSettings);
         mSurfaceSet = new HashSet<Surface>();
         mIsReprocess = false;
         mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE;
@@ -212,6 +231,7 @@
     @SuppressWarnings("unchecked")
     private CaptureRequest(CaptureRequest source) {
         mSettings = new CameraMetadataNative(source.mSettings);
+        setNativeInstance(mSettings);
         mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
         mIsReprocess = source.mIsReprocess;
         mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList;
@@ -242,6 +262,7 @@
     private CaptureRequest(CameraMetadataNative settings, boolean isReprocess,
             int reprocessableSessionId) {
         mSettings = CameraMetadataNative.move(settings);
+        setNativeInstance(mSettings);
         mSurfaceSet = new HashSet<Surface>();
         mIsReprocess = isReprocess;
         if (isReprocess) {
@@ -441,6 +462,7 @@
      */
     private void readFromParcel(Parcel in) {
         mSettings.readFromParcel(in);
+        setNativeInstance(mSettings);
 
         mSurfaceSet.clear();
 
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 3f8b57a..419e3e2 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -78,6 +78,15 @@
          *
          * @hide
          */
+        public Key(String name, Class<T> type, long vendorId) {
+            mKey = new CameraMetadataNative.Key<T>(name, type, vendorId);
+        }
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
         public Key(String name, Class<T> type) {
             mKey = new CameraMetadataNative.Key<T>(name, type);
         }
@@ -110,6 +119,15 @@
         }
 
         /**
+         * Return vendor tag id.
+         *
+         * @hide
+         */
+        public long getVendorId() {
+            return mKey.getVendorId();
+        }
+
+        /**
          * {@inheritDoc}
          */
         @Override
@@ -186,6 +204,7 @@
         if (mResults.isEmpty()) {
             throw new AssertionError("Results must not be empty");
         }
+        setNativeInstance(mResults);
         mRequest = parent;
         mSequenceId = extras.getRequestId();
         mFrameNumber = extras.getFrameNumber();
@@ -215,6 +234,7 @@
             throw new AssertionError("Results must not be empty");
         }
 
+        setNativeInstance(mResults);
         mRequest = null;
         mSequenceId = sequenceId;
         mFrameNumber = -1;
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 4d92ab1..ebe2fa1 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -79,10 +79,28 @@
     public static class Key<T> {
         private boolean mHasTag;
         private int mTag;
+        private long mVendorId = Long.MAX_VALUE;
         private final Class<T> mType;
         private final TypeReference<T> mTypeReference;
         private final String mName;
         private final int mHash;
+
+        /**
+         * @hide
+         */
+        public Key(String name, Class<T> type, long vendorId) {
+            if (name == null) {
+                throw new NullPointerException("Key needs a valid name");
+            } else if (type == null) {
+                throw new NullPointerException("Type needs to be non-null");
+            }
+            mName = name;
+            mType = type;
+            mVendorId = vendorId;
+            mTypeReference = TypeReference.createSpecializedTypeReference(type);
+            mHash = mName.hashCode() ^ mTypeReference.hashCode();
+        }
+
         /**
          * Visible for testing only.
          *
@@ -194,7 +212,7 @@
          */
         public final int getTag() {
             if (!mHasTag) {
-                mTag = CameraMetadataNative.getTag(mName);
+                mTag = CameraMetadataNative.getTag(mName, mVendorId);
                 mHasTag = true;
             }
             return mTag;
@@ -212,6 +230,15 @@
         }
 
         /**
+         * Get the vendor tag provider id.
+         *
+         * @hide
+         */
+        public final long getVendorId() {
+            return mVendorId;
+        }
+
+        /**
          * Get the type reference backing the type {@code T} for this key.
          *
          * <p>The distinction is only important if {@code T} is a generic, e.g.
@@ -463,13 +490,14 @@
     }
 
     private <T> T getBase(Key<T> key) {
-        int tag = key.getTag();
+        int tag = nativeGetTagFromKeyLocal(key.getName());
         byte[] values = readValues(tag);
         if (values == null) {
             return null;
         }
 
-        Marshaler<T> marshaler = getMarshalerForKey(key);
+        int nativeType = nativeGetTypeFromTagLocal(tag);
+        Marshaler<T> marshaler = getMarshalerForKey(key, nativeType);
         ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
         return marshaler.unmarshal(buffer);
     }
@@ -947,15 +975,15 @@
     }
 
     private <T> void setBase(Key<T> key, T value) {
-        int tag = key.getTag();
-
+        int tag = nativeGetTagFromKeyLocal(key.getName());
         if (value == null) {
             // Erase the entry
             writeValues(tag, /*src*/null);
             return;
         } // else update the entry to a new value
 
-        Marshaler<T> marshaler = getMarshalerForKey(key);
+        int nativeType = nativeGetTypeFromTagLocal(tag);
+        Marshaler<T> marshaler = getMarshalerForKey(key, nativeType);
         int size = marshaler.calculateMarshalSize(value);
 
         // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
@@ -1092,10 +1120,14 @@
     private native synchronized void nativeWriteValues(int tag, byte[] src);
     private native synchronized void nativeDump() throws IOException; // dump to ALOGD
 
-    private static native ArrayList nativeGetAllVendorKeys(Class keyClass);
-    private static native int nativeGetTagFromKey(String keyName)
+    private native synchronized ArrayList nativeGetAllVendorKeys(Class keyClass);
+    private native synchronized int nativeGetTagFromKeyLocal(String keyName)
             throws IllegalArgumentException;
-    private static native int nativeGetTypeFromTag(int tag)
+    private native synchronized int nativeGetTypeFromTagLocal(int tag)
+            throws IllegalArgumentException;
+    private static native int nativeGetTagFromKey(String keyName, long vendorId)
+            throws IllegalArgumentException;
+    private static native int nativeGetTypeFromTag(int tag, long vendorId)
             throws IllegalArgumentException;
 
     /**
@@ -1133,7 +1165,7 @@
      *
      * @hide
      */
-    public static <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) {
+    public <K>  ArrayList<K> getAllVendorKeys(Class<K> keyClass) {
         if (keyClass == null) {
             throw new NullPointerException();
         }
@@ -1149,19 +1181,32 @@
      * @hide
      */
     public static int getTag(String key) {
-        return nativeGetTagFromKey(key);
+        return nativeGetTagFromKey(key, Long.MAX_VALUE);
+    }
+
+    /**
+     * Convert a key string into the equivalent native tag.
+     *
+     * @throws IllegalArgumentException if the key was not recognized
+     * @throws NullPointerException if the key was null
+     *
+     * @hide
+     */
+    public static int getTag(String key, long vendorId) {
+        return nativeGetTagFromKey(key, vendorId);
     }
 
     /**
      * Get the underlying native type for a tag.
      *
      * @param tag An integer tag, see e.g. {@link #getTag}
+     * @param vendorId A vendor tag provider id
      * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE}
      *
      * @hide
      */
-    public static int getNativeType(int tag) {
-        return nativeGetTypeFromTag(tag);
+    public static int getNativeType(int tag, long vendorId) {
+        return nativeGetTypeFromTag(tag, vendorId);
     }
 
     /**
@@ -1226,9 +1271,9 @@
      * @throws UnsupportedOperationException
      *          if the native/managed type combination for {@code key} is not supported
      */
-    private static <T> Marshaler<T> getMarshalerForKey(Key<T> key) {
+    private static <T> Marshaler<T> getMarshalerForKey(Key<T> key, int nativeType) {
         return MarshalRegistry.getMarshaler(key.getTypeReference(),
-                getNativeType(key.getTag()));
+                nativeType);
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
diff --git a/core/java/android/hardware/camera2/params/VendorTagDescriptorCache.java b/core/java/android/hardware/camera2/params/VendorTagDescriptorCache.java
new file mode 100644
index 0000000..1f92f6d
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/VendorTagDescriptorCache.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.params;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * A class for describing the vendor tag cache declared by a camera HAL module.
+ * Generally only used by the native side of
+ * android.hardware.camera2.impl.CameraMetadataNative
+ *
+ * @hide
+ */
+public final class VendorTagDescriptorCache implements Parcelable {
+
+    private VendorTagDescriptorCache(Parcel source) {
+    }
+
+    public static final Parcelable.Creator<VendorTagDescriptorCache> CREATOR =
+            new Parcelable.Creator<VendorTagDescriptorCache>() {
+        @Override
+        public VendorTagDescriptorCache createFromParcel(Parcel source) {
+            try {
+                VendorTagDescriptorCache vendorDescriptorCache = new VendorTagDescriptorCache(source);
+                return vendorDescriptorCache;
+            } catch (Exception e) {
+                Log.e(TAG, "Exception creating VendorTagDescriptorCache from parcel", e);
+                return null;
+            }
+        }
+
+        @Override
+        public VendorTagDescriptorCache[] newArray(int size) {
+            return new VendorTagDescriptorCache[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (dest == null) {
+            throw new IllegalArgumentException("dest must not be null");
+        }
+    }
+
+    private static final String TAG = "VendorTagDescriptorCache";
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index da5d04d..33fabfc 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -208,6 +208,7 @@
     $(TOP)/system/core/include \
     $(TOP)/system/core/libappfuse/include \
     $(TOP)/system/media/camera/include \
+    $(TOP)/system/media/private/camera/include \
     $(TOP)/system/netd/include \
     external/giflib \
     external/pdfium/public \
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 78a5735..c11ce0f 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -36,6 +36,7 @@
 #include <android/hardware/ICameraService.h>
 #include <binder/IServiceManager.h>
 #include <camera/CameraMetadata.h>
+#include <camera_metadata_hidden.h>
 #include <camera/VendorTagDescriptor.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include <nativehelper/ScopedPrimitiveArray.h>
@@ -162,8 +163,10 @@
 extern "C" {
 
 static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType);
-static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName);
-static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag);
+static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName, jlong vendorId);
+static jint CameraMetadata_getTagFromKeyLocal(JNIEnv *env, jobject thiz, jstring keyName);
+static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag, jlong vendorId);
+static jint CameraMetadata_getTypeFromTagLocal(JNIEnv *env, jobject thiz, jint tag);
 static jint CameraMetadata_setupGlobalVendorTagDescriptor(JNIEnv *env, jobject thiz);
 
 // Less safe access to native pointer. Does NOT throw any Java exceptions if NULL.
@@ -286,7 +289,9 @@
     CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
     if (metadata == NULL) return NULL;
 
-    int tagType = get_camera_metadata_tag_type(tag);
+    const camera_metadata_t *metaBuffer = metadata->getAndLock();
+    int tagType = get_local_camera_metadata_tag_type(tag, metaBuffer);
+    metadata->unlock(metaBuffer);
     if (tagType == -1) {
         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
                              "Tag (%d) did not have a type", tag);
@@ -323,7 +328,9 @@
     CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
     if (metadata == NULL) return;
 
-    int tagType = get_camera_metadata_tag_type(tag);
+    const camera_metadata_t *metaBuffer = metadata->getAndLock();
+    int tagType = get_local_camera_metadata_tag_type(tag, metaBuffer);
+    metadata->unlock(metaBuffer);
     if (tagType == -1) {
         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
                              "Tag (%d) did not have a type", tag);
@@ -528,14 +535,11 @@
 
 static const JNINativeMethod gCameraMetadataMethods[] = {
 // static methods
-  { "nativeGetAllVendorKeys",
-    "(Ljava/lang/Class;)Ljava/util/ArrayList;",
-    (void *)CameraMetadata_getAllVendorKeys},
   { "nativeGetTagFromKey",
-    "(Ljava/lang/String;)I",
+    "(Ljava/lang/String;J)I",
     (void *)CameraMetadata_getTagFromKey },
   { "nativeGetTypeFromTag",
-    "(I)I",
+    "(IJ)I",
     (void *)CameraMetadata_getTypeFromTag },
   { "nativeSetupGlobalVendorTagDescriptor",
     "()I",
@@ -559,6 +563,12 @@
   { "nativeSwap",
     "(L" CAMERA_METADATA_CLASS_NAME ";)V",
     (void *)CameraMetadata_swap },
+  { "nativeGetTagFromKeyLocal",
+    "(Ljava/lang/String;)I",
+    (void *)CameraMetadata_getTagFromKeyLocal },
+  { "nativeGetTypeFromTagLocal",
+    "(I)I",
+    (void *)CameraMetadata_getTypeFromTagLocal },
   { "nativeReadValues",
     "(I)[B",
     (void *)CameraMetadata_readValues },
@@ -568,6 +578,9 @@
   { "nativeDump",
     "()V",
     (void *)CameraMetadata_dump },
+  { "nativeGetAllVendorKeys",
+    "(Ljava/lang/Class;)Ljava/util/ArrayList;",
+    (void *)CameraMetadata_getAllVendorKeys},
 // Parcelable interface
   { "nativeReadFromParcel",
     "(Landroid/os/Parcel;)V",
@@ -590,11 +603,11 @@
     gMetadataOffsets.mResultKey = MakeGlobalRefOrDie(env, resultKeyClazz);
     gMetadataOffsets.mCharacteristicsConstr = GetMethodIDOrDie(env,
             gMetadataOffsets.mCharacteristicsKey, "<init>",
-            "(Ljava/lang/String;Ljava/lang/Class;)V");
+            "(Ljava/lang/String;Ljava/lang/Class;J)V");
     gMetadataOffsets.mRequestConstr = GetMethodIDOrDie(env,
-            gMetadataOffsets.mRequestKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V");
+            gMetadataOffsets.mRequestKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;J)V");
     gMetadataOffsets.mResultConstr = GetMethodIDOrDie(env,
-            gMetadataOffsets.mResultKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V");
+            gMetadataOffsets.mResultKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;J)V");
 
     // Store global references for primitive array types used by Keys
     jclass byteClazz = FindClassOrDie(env, "[B");
@@ -630,13 +643,76 @@
 
 extern "C" {
 
-static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType) {
+static jint CameraMetadata_getTypeFromTagLocal(JNIEnv *env, jobject thiz, jint tag) {
+    CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
+    metadata_vendor_id_t vendorId = CAMERA_METADATA_INVALID_VENDOR_ID;
+    if (metadata) {
+        const camera_metadata_t *metaBuffer = metadata->getAndLock();
+        vendorId = get_camera_metadata_vendor_id(metaBuffer);
+        metadata->unlock(metaBuffer);
+    }
 
+    int tagType = get_local_camera_metadata_tag_type_vendor_id(tag, vendorId);
+    if (tagType == -1) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                             "Tag (%d) did not have a type", tag);
+        return -1;
+    }
+
+    return tagType;
+}
+
+static jint CameraMetadata_getTagFromKeyLocal(JNIEnv *env, jobject thiz, jstring keyName) {
+    ScopedUtfChars keyScoped(env, keyName);
+    const char *key = keyScoped.c_str();
+    if (key == NULL) {
+        // exception thrown by ScopedUtfChars
+        return 0;
+    }
+    ALOGV("%s (key = '%s')", __FUNCTION__, key);
+
+    uint32_t tag = 0;
+    sp<VendorTagDescriptor> vTags;
+    CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
+    if (metadata) {
+        sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache();
+        if (cache.get()) {
+            const camera_metadata_t *metaBuffer = metadata->getAndLock();
+            metadata_vendor_id_t vendorId = get_camera_metadata_vendor_id(metaBuffer);
+            metadata->unlock(metaBuffer);
+            cache->getVendorTagDescriptor(vendorId, &vTags);
+        }
+    }
+
+    status_t res = CameraMetadata::getTagFromName(key, vTags.get(), &tag);
+    if (res != OK) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                             "Could not find tag for key '%s')", key);
+    }
+    return tag;
+}
+
+static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType) {
+    metadata_vendor_id_t vendorId = CAMERA_METADATA_INVALID_VENDOR_ID;
     // Get all vendor tags
     sp<VendorTagDescriptor> vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor();
     if (vTags.get() == nullptr) {
-        // No vendor tags.
-        return NULL;
+        sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache();
+        if (cache.get() == nullptr) {
+            // No vendor tags.
+            return nullptr;
+        }
+
+        CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
+        if (metadata == NULL) return NULL;
+
+        const camera_metadata_t *metaBuffer = metadata->getAndLock();
+        vendorId = get_camera_metadata_vendor_id(metaBuffer);
+        cache->getVendorTagDescriptor(vendorId, &vTags);
+        metadata->unlock(metaBuffer);
+        if (vTags.get() == nullptr) {
+            return nullptr;
+        }
     }
 
     int count = vTags->getTagCount();
@@ -714,7 +790,7 @@
                 return NULL;
         }
 
-        jobject key = env->NewObject(keyClazz, keyConstr, name, valueClazz);
+        jobject key = env->NewObject(keyClazz, keyConstr, name, valueClazz, vendorId);
         if (env->ExceptionCheck()) {
             return NULL;
         }
@@ -731,8 +807,8 @@
     return arrayList;
 }
 
-static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName) {
-
+static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName,
+        jlong vendorId) {
     ScopedUtfChars keyScoped(env, keyName);
     const char *key = keyScoped.c_str();
     if (key == NULL) {
@@ -744,6 +820,13 @@
     uint32_t tag = 0;
     sp<VendorTagDescriptor> vTags =
             VendorTagDescriptor::getGlobalVendorTagDescriptor();
+    if (vTags.get() == nullptr) {
+        sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache();
+        if (cache.get() != nullptr) {
+            cache->getVendorTagDescriptor(vendorId, &vTags);
+        }
+    }
+
     status_t res = CameraMetadata::getTagFromName(key, vTags.get(), &tag);
     if (res != OK) {
         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
@@ -752,8 +835,8 @@
     return tag;
 }
 
-static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag) {
-    int tagType = get_camera_metadata_tag_type(tag);
+static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag, jlong vendorId) {
+    int tagType = get_local_camera_metadata_tag_type_vendor_id(tag, vendorId);
     if (tagType == -1) {
         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
                              "Tag (%d) did not have a type", tag);
@@ -787,8 +870,24 @@
                 __FUNCTION__, res.toString8().string());
         return res.serviceSpecificErrorCode();
     }
+    if (0 < desc->getTagCount()) {
+        err = VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc);
+    } else {
+        sp<VendorTagDescriptorCache> cache = new VendorTagDescriptorCache();
+        binder::Status res = cameraService->getCameraVendorTagCache(/*out*/cache.get());
+        if (res.serviceSpecificErrorCode() == hardware::ICameraService::ERROR_DISCONNECTED) {
+            // No camera module available, not an error on devices with no cameras
+            VendorTagDescriptorCache::clearGlobalVendorTagCache();
+            return OK;
+        } else if (!res.isOk()) {
+            VendorTagDescriptorCache::clearGlobalVendorTagCache();
+            ALOGE("%s: Failed to setup vendor tag cache: %s",
+                    __FUNCTION__, res.toString8().string());
+            return res.serviceSpecificErrorCode();
+        }
 
-    err = VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc);
+        err = VendorTagDescriptorCache::setAsGlobalVendorTagCache(cache);
+    }
 
     if (err != OK) {
         return hardware::ICameraService::ERROR_INVALID_OPERATION;