Make vendor keys visible in Camera2 java APIs.

- Add vendor keys to getKeys() calls for CameraCharacteristics,
  CaptureRequest, and CaptureResult.
- Vendors can specify whether custom keys show up by listing
  visible keys in the REQUEST_AVAILABLE_RESULT_KEYS field.
- Vendor key types are always treated as a primitive (or Rational)
  array type corresponding to one of the valid types for
  a camera metadata entry.

Bug: 22067625
Change-Id: I6e7dd3db7a8bf533c2ec15ff69ca38824134e971
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index af1367c..c580083 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -173,6 +173,28 @@
             }
         }
 
+        ArrayList<TKey> vendorKeys = CameraMetadataNative.getAllVendorKeys(keyClass);
+
+        if (vendorKeys != null) {
+            for (TKey k : vendorKeys) {
+                String keyName;
+                if (k instanceof CaptureRequest.Key<?>) {
+                    keyName = ((CaptureRequest.Key<?>) k).getName();
+                } else if (k instanceof CaptureResult.Key<?>) {
+                    keyName = ((CaptureResult.Key<?>) k).getName();
+                } else if (k instanceof CameraCharacteristics.Key<?>) {
+                    keyName = ((CameraCharacteristics.Key<?>) k).getName();
+                } else {
+                    continue;
+                }
+
+                if (filterTags == null || Arrays.binarySearch(filterTags,
+                        CameraMetadataNative.getTag(keyName)) >= 0) {
+                    keyList.add(k);
+                }
+            }
+        }
+
         return keyList;
     }
 
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 7e50fd9..12a2910 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1078,6 +1078,7 @@
     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)
             throws IllegalArgumentException;
     private static native int nativeGetTypeFromTag(int tag)
@@ -1113,6 +1114,19 @@
         return nativeIsEmpty();
     }
 
+
+    /**
+     * Return a list containing keys of the given key class for all defined vendor tags.
+     *
+     * @hide
+     */
+    public static <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) {
+        if (keyClass == null) {
+            throw new NullPointerException();
+        }
+        return (ArrayList<K>) nativeGetAllVendorKeys(keyClass);
+    }
+
     /**
      * Convert a key string into the equivalent native tag.
      *
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 7c8769d..fb22689 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -23,7 +23,9 @@
 #include <utils/Vector.h>
 #include <utils/SortedVector.h>
 #include <utils/KeyedVector.h>
+#include <stdio.h>
 #include <string.h>
+#include <vector>
 
 #include "jni.h"
 #include "JNIHelp.h"
@@ -45,9 +47,30 @@
 
 // fully-qualified class name
 #define CAMERA_METADATA_CLASS_NAME "android/hardware/camera2/impl/CameraMetadataNative"
+#define CHARACTERISTICS_KEY_CLASS_NAME "android/hardware/camera2/CameraCharacteristics$Key"
+#define REQUEST_KEY_CLASS_NAME "android/hardware/camera2/CaptureRequest$Key"
+#define RESULT_KEY_CLASS_NAME "android/hardware/camera2/CaptureResult$Key"
 
 using namespace android;
 
+static struct metadata_java_key_offsets_t {
+    jclass mCharacteristicsKey;
+    jclass mResultKey;
+    jclass mRequestKey;
+    jmethodID mCharacteristicsConstr;
+    jmethodID mResultConstr;
+    jmethodID mRequestConstr;
+    jclass mByteArray;
+    jclass mInt32Array;
+    jclass mFloatArray;
+    jclass mInt64Array;
+    jclass mDoubleArray;
+    jclass mRationalArray;
+    jclass mArrayList;
+    jmethodID mArrayListConstr;
+    jmethodID mArrayListAdd;
+} gMetadataOffsets;
+
 struct fields_t {
     jfieldID    metadata_ptr;
 };
@@ -141,6 +164,7 @@
 extern "C" {
 
 static void CameraMetadata_classInit(JNIEnv *env, jobject thiz);
+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_setupGlobalVendorTagDescriptor(JNIEnv *env, jobject thiz);
@@ -510,6 +534,9 @@
   { "nativeClassInit",
     "()V",
     (void *)CameraMetadata_classInit },
+  { "nativeGetAllVendorKeys",
+    "(Ljava/lang/Class;)Ljava/util/ArrayList;",
+    (void *)CameraMetadata_getAllVendorKeys},
   { "nativeGetTagFromKey",
     "(Ljava/lang/String;)I",
     (void *)CameraMetadata_getTagFromKey },
@@ -588,6 +615,44 @@
 // Get all the required offsets in java class and register native functions
 int register_android_hardware_camera2_CameraMetadata(JNIEnv *env)
 {
+
+    // Store global references to Key-related classes and methods used natively
+    jclass characteristicsKeyClazz = FindClassOrDie(env, CHARACTERISTICS_KEY_CLASS_NAME);
+    jclass requestKeyClazz = FindClassOrDie(env, REQUEST_KEY_CLASS_NAME);
+    jclass resultKeyClazz = FindClassOrDie(env, RESULT_KEY_CLASS_NAME);
+    gMetadataOffsets.mCharacteristicsKey = MakeGlobalRefOrDie(env, characteristicsKeyClazz);
+    gMetadataOffsets.mRequestKey = MakeGlobalRefOrDie(env, requestKeyClazz);
+    gMetadataOffsets.mResultKey = MakeGlobalRefOrDie(env, resultKeyClazz);
+    gMetadataOffsets.mCharacteristicsConstr = GetMethodIDOrDie(env,
+            gMetadataOffsets.mCharacteristicsKey, "<init>",
+            "(Ljava/lang/String;Ljava/lang/Class;)V");
+    gMetadataOffsets.mRequestConstr = GetMethodIDOrDie(env,
+            gMetadataOffsets.mRequestKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V");
+    gMetadataOffsets.mResultConstr = GetMethodIDOrDie(env,
+            gMetadataOffsets.mResultKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V");
+
+    // Store global references for primitive array types used by Keys
+    jclass byteClazz = FindClassOrDie(env, "[B");
+    jclass int32Clazz = FindClassOrDie(env, "[I");
+    jclass floatClazz = FindClassOrDie(env, "[F");
+    jclass int64Clazz = FindClassOrDie(env, "[J");
+    jclass doubleClazz = FindClassOrDie(env, "[D");
+    jclass rationalClazz = FindClassOrDie(env, "[Landroid/util/Rational;");
+    gMetadataOffsets.mByteArray = MakeGlobalRefOrDie(env, byteClazz);
+    gMetadataOffsets.mInt32Array = MakeGlobalRefOrDie(env, int32Clazz);
+    gMetadataOffsets.mFloatArray = MakeGlobalRefOrDie(env, floatClazz);
+    gMetadataOffsets.mInt64Array = MakeGlobalRefOrDie(env, int64Clazz);
+    gMetadataOffsets.mDoubleArray = MakeGlobalRefOrDie(env, doubleClazz);
+    gMetadataOffsets.mRationalArray = MakeGlobalRefOrDie(env, rationalClazz);
+
+    // Store global references for ArrayList methods used
+    jclass arrayListClazz = FindClassOrDie(env, "java/util/ArrayList");
+    gMetadataOffsets.mArrayList = MakeGlobalRefOrDie(env, arrayListClazz);
+    gMetadataOffsets.mArrayListConstr = GetMethodIDOrDie(env, gMetadataOffsets.mArrayList,
+            "<init>", "(I)V");
+    gMetadataOffsets.mArrayListAdd = GetMethodIDOrDie(env, gMetadataOffsets.mArrayList,
+            "add", "(Ljava/lang/Object;)Z");
+
     // Register native functions
     return RegisterMethodsOrDie(env,
             CAMERA_METADATA_CLASS_NAME,
@@ -596,6 +661,7 @@
 }
 
 extern "C" {
+
 static void CameraMetadata_classInit(JNIEnv *env, jobject thiz) {
     // XX: Why do this separately instead of doing it in the register function?
     ALOGV("%s", __FUNCTION__);
@@ -612,6 +678,107 @@
     env->FindClass(CAMERA_METADATA_CLASS_NAME);
 }
 
+static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType) {
+
+    // Get all vendor tags
+    sp<VendorTagDescriptor> vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor();
+    if (vTags.get() == nullptr) {
+        // No vendor tags.
+        return NULL;
+    }
+
+    int count = vTags->getTagCount();
+    if (count <= 0) {
+        // No vendor tags.
+        return NULL;
+    }
+
+    std::vector<uint32_t> tagIds(count, /*initializer value*/0);
+    vTags->getTagArray(&tagIds[0]);
+
+    // Which key class/constructor should we use?
+    jclass keyClazz;
+    jmethodID keyConstr;
+    if (env->IsSameObject(keyType, gMetadataOffsets.mCharacteristicsKey)) {
+        keyClazz = gMetadataOffsets.mCharacteristicsKey;
+        keyConstr = gMetadataOffsets.mCharacteristicsConstr;
+    } else if (env->IsSameObject(keyType, gMetadataOffsets.mResultKey)) {
+        keyClazz = gMetadataOffsets.mResultKey;
+        keyConstr = gMetadataOffsets.mResultConstr;
+    } else if (env->IsSameObject(keyType, gMetadataOffsets.mRequestKey)) {
+        keyClazz = gMetadataOffsets.mRequestKey;
+        keyConstr = gMetadataOffsets.mRequestConstr;
+    } else {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                "Invalid key class given as argument.");
+        return NULL;
+    }
+
+    // Allocate arrayList to return
+    jobject arrayList = env->NewObject(gMetadataOffsets.mArrayList,
+            gMetadataOffsets.mArrayListConstr, static_cast<jint>(count));
+    if (env->ExceptionCheck()) {
+        return NULL;
+    }
+
+    for (uint32_t id : tagIds) {
+        const char* section = vTags->getSectionName(id);
+        const char* tag = vTags->getTagName(id);
+        int type = vTags->getTagType(id);
+
+        size_t totalLen = strlen(section) + strlen(tag) + 2;
+        std::vector<char> fullName(totalLen, 0);
+        snprintf(&fullName[0], totalLen, "%s.%s", section, tag);
+
+        jstring name = env->NewStringUTF(&fullName[0]);
+
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+
+        jclass valueClazz;
+        switch (type) {
+            case TYPE_BYTE:
+                valueClazz = gMetadataOffsets.mByteArray;
+                break;
+            case TYPE_INT32:
+                valueClazz = gMetadataOffsets.mInt32Array;
+                break;
+            case TYPE_FLOAT:
+                valueClazz = gMetadataOffsets.mFloatArray;
+                break;
+            case TYPE_INT64:
+                valueClazz = gMetadataOffsets.mInt64Array;
+                break;
+            case TYPE_DOUBLE:
+                valueClazz = gMetadataOffsets.mDoubleArray;
+                break;
+            case TYPE_RATIONAL:
+                valueClazz = gMetadataOffsets.mRationalArray;
+                break;
+            default:
+                jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                        "Invalid type %d given for key %s", type, &fullName[0]);
+                return NULL;
+        }
+
+        jobject key = env->NewObject(keyClazz, keyConstr, name, valueClazz);
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+
+        env->CallBooleanMethod(arrayList, gMetadataOffsets.mArrayListAdd, key);
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+
+        env->DeleteLocalRef(name);
+        env->DeleteLocalRef(key);
+    }
+
+    return arrayList;
+}
+
 static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName) {
 
     ScopedUtfChars keyScoped(env, keyName);