/* ** ** Copyright 2013, 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. */ // #define LOG_NDEBUG 0 #define LOG_TAG "CameraMetadata-JNI" #include #include #include #include #include #include #include #include #include #include "jni.h" #include #include "android_os_Parcel.h" #include "core_jni_helpers.h" #include "android_runtime/android_hardware_camera2_CameraMetadata.h" #include #include #include #include #include #include #include #include // for socketpair #include // for socketpair // 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; }; static fields_t fields; namespace android { status_t CameraMetadata_getNativeMetadata(JNIEnv* env, jobject thiz, /*out*/CameraMetadata* metadata) { if (!thiz) { ALOGE("%s: Invalid java metadata object.", __FUNCTION__); return BAD_VALUE; } if (!metadata) { ALOGE("%s: Invalid output metadata object.", __FUNCTION__); return BAD_VALUE; } CameraMetadata* nativePtr = reinterpret_cast(env->GetLongField(thiz, fields.metadata_ptr)); if (nativePtr == NULL) { ALOGE("%s: Invalid native pointer in java metadata object.", __FUNCTION__); return BAD_VALUE; } *metadata = *nativePtr; return OK; } } /*namespace android*/ namespace { struct Helpers { static size_t getTypeSize(uint8_t type) { if (type >= NUM_TYPES) { ALOGE("%s: Invalid type specified (%ud)", __FUNCTION__, type); return static_cast(-1); } return camera_metadata_type_size[type]; } static status_t updateAny(CameraMetadata *metadata, uint32_t tag, uint32_t type, const void *data, size_t dataBytes) { if (type >= NUM_TYPES) { ALOGE("%s: Invalid type specified (%ud)", __FUNCTION__, type); return INVALID_OPERATION; } size_t typeSize = getTypeSize(type); if (dataBytes % typeSize != 0) { ALOGE("%s: Expected dataBytes (%zu) to be divisible by typeSize " "(%zu)", __FUNCTION__, dataBytes, typeSize); return BAD_VALUE; } size_t dataCount = dataBytes / typeSize; switch(type) { #define METADATA_UPDATE(runtime_type, compile_type) \ case runtime_type: { \ const compile_type *dataPtr = \ static_cast(data); \ return metadata->update(tag, dataPtr, dataCount); \ } \ METADATA_UPDATE(TYPE_BYTE, uint8_t); METADATA_UPDATE(TYPE_INT32, int32_t); METADATA_UPDATE(TYPE_FLOAT, float); METADATA_UPDATE(TYPE_INT64, int64_t); METADATA_UPDATE(TYPE_DOUBLE, double); METADATA_UPDATE(TYPE_RATIONAL, camera_metadata_rational_t); default: { // unreachable ALOGE("%s: Unreachable", __FUNCTION__); return INVALID_OPERATION; } } #undef METADATA_UPDATE } }; } // namespace {} extern "C" { static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType); 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. static CameraMetadata* CameraMetadata_getPointerNoThrow(JNIEnv *env, jobject thiz) { if (thiz == NULL) { return NULL; } return reinterpret_cast(env->GetLongField(thiz, fields.metadata_ptr)); } // Safe access to native pointer from object. Throws if not possible to access. static CameraMetadata* CameraMetadata_getPointerThrow(JNIEnv *env, jobject thiz, const char* argName = "this") { if (thiz == NULL) { ALOGV("%s: Throwing java.lang.NullPointerException for null reference", __FUNCTION__); jniThrowNullPointerException(env, argName); return NULL; } CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz); if (metadata == NULL) { ALOGV("%s: Throwing java.lang.IllegalStateException for closed object", __FUNCTION__); jniThrowException(env, "java/lang/IllegalStateException", "Metadata object was already closed"); return NULL; } return metadata; } static jlong CameraMetadata_allocate(JNIEnv *env, jobject thiz) { ALOGV("%s", __FUNCTION__); return reinterpret_cast(new CameraMetadata()); } static jlong CameraMetadata_allocateCopy(JNIEnv *env, jobject thiz, jobject other) { ALOGV("%s", __FUNCTION__); CameraMetadata* otherMetadata = CameraMetadata_getPointerThrow(env, other, "other"); // In case of exception, return if (otherMetadata == NULL) return NULL; // Clone native metadata and return new pointer return reinterpret_cast(new CameraMetadata(*otherMetadata)); } static jboolean CameraMetadata_isEmpty(JNIEnv *env, jobject thiz) { ALOGV("%s", __FUNCTION__); CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); if (metadata == NULL) { ALOGW("%s: Returning early due to exception being thrown", __FUNCTION__); return JNI_TRUE; // actually throws java exc. } jboolean empty = metadata->isEmpty(); ALOGV("%s: Empty returned %d, entry count was %zu", __FUNCTION__, empty, metadata->entryCount()); return empty; } static jint CameraMetadata_getEntryCount(JNIEnv *env, jobject thiz) { ALOGV("%s", __FUNCTION__); CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); if (metadata == NULL) return 0; // actually throws java exc. return metadata->entryCount(); } // idempotent. calling more than once has no effect. static void CameraMetadata_close(JNIEnv *env, jobject thiz) { ALOGV("%s", __FUNCTION__); CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz); if (metadata != NULL) { delete metadata; env->SetLongField(thiz, fields.metadata_ptr, 0); } LOG_ALWAYS_FATAL_IF(CameraMetadata_getPointerNoThrow(env, thiz) != NULL, "Expected the native ptr to be 0 after #close"); } static void CameraMetadata_swap(JNIEnv *env, jobject thiz, jobject other) { ALOGV("%s", __FUNCTION__); CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); // order is important: we can't call another JNI method // if there is an exception pending if (metadata == NULL) return; CameraMetadata* otherMetadata = CameraMetadata_getPointerThrow(env, other, "other"); if (otherMetadata == NULL) return; metadata->swap(*otherMetadata); } static jbyteArray CameraMetadata_readValues(JNIEnv *env, jobject thiz, jint tag) { ALOGV("%s (tag = %d)", __FUNCTION__, tag); CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); if (metadata == NULL) return NULL; 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); return NULL; } size_t tagSize = Helpers::getTypeSize(tagType); camera_metadata_entry entry = metadata->find(tag); if (entry.count == 0) { if (!metadata->exists(tag)) { ALOGV("%s: Tag %d does not have any entries", __FUNCTION__, tag); return NULL; } else { // OK: we will return a 0-sized array. ALOGV("%s: Tag %d had an entry, but it had 0 data", __FUNCTION__, tag); } } jsize byteCount = entry.count * tagSize; jbyteArray byteArray = env->NewByteArray(byteCount); if (env->ExceptionCheck()) return NULL; // Copy into java array from native array ScopedByteArrayRW arrayWriter(env, byteArray); memcpy(arrayWriter.get(), entry.data.u8, byteCount); return byteArray; } static void CameraMetadata_writeValues(JNIEnv *env, jobject thiz, jint tag, jbyteArray src) { ALOGV("%s (tag = %d)", __FUNCTION__, tag); CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); if (metadata == NULL) return; 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); return; } status_t res; if (src == NULL) { // If array is NULL, delete the entry if (metadata->exists(tag)) { res = metadata->erase(tag); ALOGV("%s: Erase values (res = %d)", __FUNCTION__, res); } else { res = OK; ALOGV("%s: Don't need to erase", __FUNCTION__); } } else { // Copy from java array into native array ScopedByteArrayRO arrayReader(env, src); if (arrayReader.get() == NULL) return; res = Helpers::updateAny(metadata, static_cast(tag), tagType, arrayReader.get(), arrayReader.size()); ALOGV("%s: Update values (res = %d)", __FUNCTION__, res); } if (res == OK) { return; } else if (res == BAD_VALUE) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Src byte array was poorly formed"); } else if (res == INVALID_OPERATION) { jniThrowExceptionFmt(env, "java/lang/IllegalStateException", "Internal error while trying to update metadata"); } else { jniThrowExceptionFmt(env, "java/lang/IllegalStateException", "Unknown error (%d) while trying to update " "metadata", res); } } struct DumpMetadataParams { int writeFd; const CameraMetadata* metadata; }; static void* CameraMetadata_writeMetadataThread(void* arg) { DumpMetadataParams* p = static_cast(arg); /* * Write the dumped data, and close the writing side FD. */ p->metadata->dump(p->writeFd, /*verbosity*/2); if (close(p->writeFd) < 0) { ALOGE("%s: Failed to close writeFd (errno = %#x, message = '%s')", __FUNCTION__, errno, strerror(errno)); } return NULL; } static void CameraMetadata_dump(JNIEnv *env, jobject thiz) { ALOGV("%s", __FUNCTION__); CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); if (metadata == NULL) { return; } /* * Create a socket pair for local streaming read/writes. * * The metadata will be dumped into the write side, * and then read back out (and logged) via the read side. */ int writeFd, readFd; { int sv[2]; if (socketpair(AF_LOCAL, SOCK_STREAM, /*protocol*/0, &sv[0]) < 0) { jniThrowExceptionFmt(env, "java/io/IOException", "Failed to create socketpair (errno = %#x, message = '%s')", errno, strerror(errno)); return; } writeFd = sv[0]; readFd = sv[1]; } /* * Create a thread for doing the writing. * * The reading and writing must be concurrent, otherwise * the write will block forever once it exhausts the capped * buffer size (from getsockopt). */ pthread_t writeThread; DumpMetadataParams params = { writeFd, metadata }; { int threadRet = pthread_create(&writeThread, /*attr*/NULL, CameraMetadata_writeMetadataThread, (void*)¶ms); if (threadRet != 0) { close(writeFd); close(readFd); jniThrowExceptionFmt(env, "java/io/IOException", "Failed to create thread for writing (errno = %#x, message = '%s')", threadRet, strerror(threadRet)); return; } } /* * Read out a byte until stream is complete. Write completed lines * to ALOG. */ { char out[] = {'\0', '\0'}; // large enough to append as a string String8 logLine; // Read one byte at a time! Very slow but avoids complicated \n scanning. ssize_t res; while ((res = TEMP_FAILURE_RETRY(read(readFd, &out[0], /*count*/1))) > 0) { if (out[0] == '\n') { ALOGD("%s", logLine.string()); logLine.clear(); } else { logLine.append(out); } } if (res < 0) { jniThrowExceptionFmt(env, "java/io/IOException", "Failed to read from fd (errno = %#x, message = '%s')", errno, strerror(errno)); //return; } else if (!logLine.isEmpty()) { ALOGD("%s", logLine.string()); } close(readFd); } int res; // Join until thread finishes. Ensures params/metadata is valid until then. if ((res = pthread_join(writeThread, /*retval*/NULL)) != 0) { ALOGE("%s: Failed to join thread (errno = %#x, message = '%s')", __FUNCTION__, res, strerror(res)); } } static void CameraMetadata_readFromParcel(JNIEnv *env, jobject thiz, jobject parcel) { ALOGV("%s", __FUNCTION__); CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); if (metadata == NULL) { return; } Parcel* parcelNative = parcelForJavaObject(env, parcel); if (parcelNative == NULL) { jniThrowNullPointerException(env, "parcel"); return; } status_t err; if ((err = metadata->readFromParcel(parcelNative)) != OK) { jniThrowExceptionFmt(env, "java/lang/IllegalStateException", "Failed to read from parcel (error code %d)", err); return; } } static void CameraMetadata_writeToParcel(JNIEnv *env, jobject thiz, jobject parcel) { ALOGV("%s", __FUNCTION__); CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); if (metadata == NULL) { return; } Parcel* parcelNative = parcelForJavaObject(env, parcel); if (parcelNative == NULL) { jniThrowNullPointerException(env, "parcel"); return; } status_t err; if ((err = metadata->writeToParcel(parcelNative)) != OK) { jniThrowExceptionFmt(env, "java/lang/IllegalStateException", "Failed to write to parcel (error code %d)", err); return; } } } // extern "C" //------------------------------------------------- static const JNINativeMethod gCameraMetadataMethods[] = { // static methods { "nativeGetTagFromKey", "(Ljava/lang/String;J)I", (void *)CameraMetadata_getTagFromKey }, { "nativeGetTypeFromTag", "(IJ)I", (void *)CameraMetadata_getTypeFromTag }, { "nativeSetupGlobalVendorTagDescriptor", "()I", (void*)CameraMetadata_setupGlobalVendorTagDescriptor }, // instance methods { "nativeAllocate", "()J", (void*)CameraMetadata_allocate }, { "nativeAllocateCopy", "(L" CAMERA_METADATA_CLASS_NAME ";)J", (void *)CameraMetadata_allocateCopy }, { "nativeIsEmpty", "()Z", (void*)CameraMetadata_isEmpty }, { "nativeGetEntryCount", "()I", (void*)CameraMetadata_getEntryCount }, { "nativeClose", "()V", (void*)CameraMetadata_close }, { "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 }, { "nativeWriteValues", "(I[B)V", (void *)CameraMetadata_writeValues }, { "nativeDump", "()V", (void *)CameraMetadata_dump }, { "nativeGetAllVendorKeys", "(Ljava/lang/Class;)Ljava/util/ArrayList;", (void *)CameraMetadata_getAllVendorKeys}, // Parcelable interface { "nativeReadFromParcel", "(Landroid/os/Parcel;)V", (void *)CameraMetadata_readFromParcel }, { "nativeWriteToParcel", "(Landroid/os/Parcel;)V", (void *)CameraMetadata_writeToParcel }, }; // 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, "", "(Ljava/lang/String;Ljava/lang/Class;J)V"); gMetadataOffsets.mRequestConstr = GetMethodIDOrDie(env, gMetadataOffsets.mRequestKey, "", "(Ljava/lang/String;Ljava/lang/Class;J)V"); gMetadataOffsets.mResultConstr = GetMethodIDOrDie(env, gMetadataOffsets.mResultKey, "", "(Ljava/lang/String;Ljava/lang/Class;J)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, "", "(I)V"); gMetadataOffsets.mArrayListAdd = GetMethodIDOrDie(env, gMetadataOffsets.mArrayList, "add", "(Ljava/lang/Object;)Z"); jclass cameraMetadataClazz = FindClassOrDie(env, CAMERA_METADATA_CLASS_NAME); fields.metadata_ptr = GetFieldIDOrDie(env, cameraMetadataClazz, "mMetadataPtr", "J"); // Register native functions return RegisterMethodsOrDie(env, CAMERA_METADATA_CLASS_NAME, gCameraMetadataMethods, NELEM(gCameraMetadataMethods)); } extern "C" { 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 vTags; CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz); if (metadata) { sp 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 vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor(); if (vTags.get() == nullptr) { sp 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(); if (count <= 0) { // No vendor tags. return NULL; } std::vector 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(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 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, vendorId); 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, jlong vendorId) { 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 vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor(); if (vTags.get() == nullptr) { sp 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", "Could not find tag for key '%s')", key); } return 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); return -1; } return tagType; } static jint CameraMetadata_setupGlobalVendorTagDescriptor(JNIEnv *env, jobject thiz) { const String16 NAME("media.camera"); sp cameraService; status_t err = getService(NAME, /*out*/&cameraService); if (err != OK) { ALOGE("%s: Failed to get camera service, received error %s (%d)", __FUNCTION__, strerror(-err), err); return hardware::ICameraService::ERROR_DISCONNECTED; } sp desc = new VendorTagDescriptor(); binder::Status res = cameraService->getCameraVendorTagDescriptor(/*out*/desc.get()); if (res.serviceSpecificErrorCode() == hardware::ICameraService::ERROR_DISCONNECTED) { // No camera module available, not an error on devices with no cameras VendorTagDescriptor::clearGlobalVendorTagDescriptor(); return OK; } else if (!res.isOk()) { VendorTagDescriptor::clearGlobalVendorTagDescriptor(); ALOGE("%s: Failed to setup vendor tag descriptors: %s", __FUNCTION__, res.toString8().string()); return res.serviceSpecificErrorCode(); } if (0 < desc->getTagCount()) { err = VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc); } else { sp 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 = VendorTagDescriptorCache::setAsGlobalVendorTagCache(cache); } if (err != OK) { return hardware::ICameraService::ERROR_INVALID_OPERATION; } return OK; } } // extern "C"