/* ** ** 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 "JNIHelp.h" #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 // for socketpair #include // for socketpair static const bool kIsDebug = false; // 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 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); // 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; int tagType = get_camera_metadata_tag_type(tag); 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; int tagType = get_camera_metadata_tag_type(tag); 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); jniThrowExceptionFmt(env, "java/io/IOException", "Failed to create thread for writing (errno = %#x, message = '%s')", threadRet, strerror(threadRet)); } } /* * 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()); } } 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 JNINativeMethod gCameraMetadataMethods[] = { // static methods { "nativeClassInit", "()V", (void *)CameraMetadata_classInit }, { "nativeGetAllVendorKeys", "(Ljava/lang/Class;)Ljava/util/ArrayList;", (void *)CameraMetadata_getAllVendorKeys}, { "nativeGetTagFromKey", "(Ljava/lang/String;)I", (void *)CameraMetadata_getTagFromKey }, { "nativeGetTypeFromTag", "(I)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 }, { "nativeReadValues", "(I)[B", (void *)CameraMetadata_readValues }, { "nativeWriteValues", "(I[B)V", (void *)CameraMetadata_writeValues }, { "nativeDump", "()V", (void *)CameraMetadata_dump }, // Parcelable interface { "nativeReadFromParcel", "(Landroid/os/Parcel;)V", (void *)CameraMetadata_readFromParcel }, { "nativeWriteToParcel", "(Landroid/os/Parcel;)V", (void *)CameraMetadata_writeToParcel }, }; struct field { const char *class_name; const char *field_name; const char *field_type; jfieldID *jfield; }; static int find_fields(JNIEnv *env, field *fields, int count) { for (int i = 0; i < count; i++) { field *f = &fields[i]; jclass clazz = env->FindClass(f->class_name); if (clazz == NULL) { ALOGE("Can't find %s", f->class_name); return -1; } jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type); if (field == NULL) { ALOGE("Can't find %s.%s", f->class_name, f->field_name); return -1; } *(f->jfield) = field; } return 0; } // 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;)V"); gMetadataOffsets.mRequestConstr = GetMethodIDOrDie(env, gMetadataOffsets.mRequestKey, "", "(Ljava/lang/String;Ljava/lang/Class;)V"); gMetadataOffsets.mResultConstr = GetMethodIDOrDie(env, gMetadataOffsets.mResultKey, "", "(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, "", "(I)V"); gMetadataOffsets.mArrayListAdd = GetMethodIDOrDie(env, gMetadataOffsets.mArrayList, "add", "(Ljava/lang/Object;)Z"); // Register native functions return RegisterMethodsOrDie(env, CAMERA_METADATA_CLASS_NAME, gCameraMetadataMethods, NELEM(gCameraMetadataMethods)); } 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__); field fields_to_find[] = { { CAMERA_METADATA_CLASS_NAME, "mMetadataPtr", "J", &fields.metadata_ptr }, }; // Do this here instead of in register_native_methods, // since otherwise it will fail to find the fields. if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0) return; env->FindClass(CAMERA_METADATA_CLASS_NAME); } static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType) { // Get all vendor tags sp 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 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); 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); const char *key = keyScoped.c_str(); if (key == NULL) { // exception thrown by ScopedUtfChars return 0; } size_t keyLength = strlen(key); ALOGV("%s (key = '%s')", __FUNCTION__, key); sp vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor(); SortedVector vendorSections; size_t vendorSectionCount = 0; if (vTags != NULL) { vendorSections = vTags->getAllSectionNames(); vendorSectionCount = vendorSections.size(); } // First, find the section by the longest string match const char *section = NULL; size_t sectionIndex = 0; size_t sectionLength = 0; size_t totalSectionCount = ANDROID_SECTION_COUNT + vendorSectionCount; for (size_t i = 0; i < totalSectionCount; ++i) { const char *str = (i < ANDROID_SECTION_COUNT) ? camera_metadata_section_names[i] : vendorSections[i - ANDROID_SECTION_COUNT].string(); if (kIsDebug) { ALOGV("%s: Trying to match against section '%s'", __FUNCTION__, str); } if (strstr(key, str) == key) { // key begins with the section name size_t strLength = strlen(str); if (kIsDebug) { ALOGV("%s: Key begins with section name", __FUNCTION__); } // section name is the longest we've found so far if (section == NULL || sectionLength < strLength) { section = str; sectionIndex = i; sectionLength = strLength; if (kIsDebug) { ALOGV("%s: Found new best section (%s)", __FUNCTION__, section); } } } } // TODO: Make above get_camera_metadata_section_from_name ? if (section == NULL) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Could not find section name for key '%s')", key); return 0; } else { ALOGV("%s: Found matched section '%s' (%zu)", __FUNCTION__, section, sectionIndex); } // Get the tag name component of the key const char *keyTagName = key + sectionLength + 1; // x.y.z -> z if (sectionLength + 1 >= keyLength) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Key length too short for key '%s')", key); return 0; } // Match rest of name against the tag names in that section only uint32_t tag = 0; if (sectionIndex < ANDROID_SECTION_COUNT) { // Match built-in tags (typically android.*) uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd) tagBegin = camera_metadata_section_bounds[sectionIndex][0]; tagEnd = camera_metadata_section_bounds[sectionIndex][1]; for (tag = tagBegin; tag < tagEnd; ++tag) { const char *tagName = get_camera_metadata_tag_name(tag); if (strcmp(keyTagName, tagName) == 0) { ALOGV("%s: Found matched tag '%s' (%d)", __FUNCTION__, tagName, tag); break; } } if (tag == tagEnd) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Could not find tag name for key '%s')", key); return 0; } } else if (vTags != NULL) { // Match vendor tags (typically com.*) const String8 sectionName(section); const String8 tagName(keyTagName); status_t res = OK; if ((res = vTags->lookupTag(tagName, sectionName, &tag)) != OK) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "%s: No vendor tag matches key '%s'", __FUNCTION__, key); return 0; } } // TODO: Make above get_camera_metadata_tag_from_name ? return tag; } static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag) { int tagType = get_camera_metadata_tag_type(tag); 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 err; } sp desc; err = cameraService->getCameraVendorTagDescriptor(/*out*/desc); if (err == -EOPNOTSUPP) { ALOGW("%s: Camera HAL too old; does not support vendor tags", __FUNCTION__); VendorTagDescriptor::clearGlobalVendorTagDescriptor(); return OK; } else if (err != OK) { ALOGE("%s: Failed to setup vendor tag descriptors, received error %s (%d)", __FUNCTION__, strerror(-err), err); return err; } err = VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc); return err; } } // extern "C"