android_hardware_camera2_CameraMetadata.cpp revision 76f6a86de25e1bf74717e047e55fd44b089673f3
1/*
2**
3** Copyright 2013, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18// #define LOG_NDEBUG 0
19#define LOG_TAG "CameraMetadata-JNI"
20#include <utils/Errors.h>
21#include <utils/Log.h>
22#include <utils/RefBase.h>
23#include <utils/Vector.h>
24#include <utils/SortedVector.h>
25#include <utils/KeyedVector.h>
26#include <string.h>
27
28#include "jni.h"
29#include "JNIHelp.h"
30#include "android_os_Parcel.h"
31#include "core_jni_helpers.h"
32#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
33
34#include <binder/IServiceManager.h>
35#include <camera/CameraMetadata.h>
36#include <camera/ICameraService.h>
37#include <camera/VendorTagDescriptor.h>
38#include <nativehelper/ScopedUtfChars.h>
39#include <nativehelper/ScopedPrimitiveArray.h>
40
41#include <sys/types.h> // for socketpair
42#include <sys/socket.h> // for socketpair
43
44static const bool kIsDebug = false;
45
46// fully-qualified class name
47#define CAMERA_METADATA_CLASS_NAME "android/hardware/camera2/impl/CameraMetadataNative"
48
49using namespace android;
50
51struct fields_t {
52    jfieldID    metadata_ptr;
53};
54
55static fields_t fields;
56
57namespace android {
58
59status_t CameraMetadata_getNativeMetadata(JNIEnv* env, jobject thiz,
60        /*out*/CameraMetadata* metadata) {
61    if (!thiz) {
62        ALOGE("%s: Invalid java metadata object.", __FUNCTION__);
63        return BAD_VALUE;
64    }
65
66    if (!metadata) {
67        ALOGE("%s: Invalid output metadata object.", __FUNCTION__);
68        return BAD_VALUE;
69    }
70    CameraMetadata* nativePtr = reinterpret_cast<CameraMetadata*>(env->GetLongField(thiz,
71            fields.metadata_ptr));
72    if (nativePtr == NULL) {
73        ALOGE("%s: Invalid native pointer in java metadata object.", __FUNCTION__);
74        return BAD_VALUE;
75    }
76    *metadata = *nativePtr;
77    return OK;
78}
79
80} /*namespace android*/
81
82namespace {
83struct Helpers {
84    static size_t getTypeSize(uint8_t type) {
85        if (type >= NUM_TYPES) {
86            ALOGE("%s: Invalid type specified (%ud)", __FUNCTION__, type);
87            return static_cast<size_t>(-1);
88        }
89
90        return camera_metadata_type_size[type];
91    }
92
93    static status_t updateAny(CameraMetadata *metadata,
94                          uint32_t tag,
95                          uint32_t type,
96                          const void *data,
97                          size_t dataBytes) {
98
99        if (type >= NUM_TYPES) {
100            ALOGE("%s: Invalid type specified (%ud)", __FUNCTION__, type);
101            return INVALID_OPERATION;
102        }
103
104        size_t typeSize = getTypeSize(type);
105
106        if (dataBytes % typeSize != 0) {
107            ALOGE("%s: Expected dataBytes (%zu) to be divisible by typeSize "
108                  "(%zu)", __FUNCTION__, dataBytes, typeSize);
109            return BAD_VALUE;
110        }
111
112        size_t dataCount = dataBytes / typeSize;
113
114        switch(type) {
115#define METADATA_UPDATE(runtime_type, compile_type)                            \
116            case runtime_type: {                                               \
117                const compile_type *dataPtr =                                  \
118                        static_cast<const compile_type*>(data);                \
119                return metadata->update(tag, dataPtr, dataCount);              \
120            }                                                                  \
121
122            METADATA_UPDATE(TYPE_BYTE,     uint8_t);
123            METADATA_UPDATE(TYPE_INT32,    int32_t);
124            METADATA_UPDATE(TYPE_FLOAT,    float);
125            METADATA_UPDATE(TYPE_INT64,    int64_t);
126            METADATA_UPDATE(TYPE_DOUBLE,   double);
127            METADATA_UPDATE(TYPE_RATIONAL, camera_metadata_rational_t);
128
129            default: {
130                // unreachable
131                ALOGE("%s: Unreachable", __FUNCTION__);
132                return INVALID_OPERATION;
133            }
134        }
135
136#undef METADATA_UPDATE
137    }
138};
139} // namespace {}
140
141extern "C" {
142
143static void CameraMetadata_classInit(JNIEnv *env, jobject thiz);
144static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName);
145static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag);
146static jint CameraMetadata_setupGlobalVendorTagDescriptor(JNIEnv *env, jobject thiz);
147
148// Less safe access to native pointer. Does NOT throw any Java exceptions if NULL.
149static CameraMetadata* CameraMetadata_getPointerNoThrow(JNIEnv *env, jobject thiz) {
150
151    if (thiz == NULL) {
152        return NULL;
153    }
154
155    return reinterpret_cast<CameraMetadata*>(env->GetLongField(thiz, fields.metadata_ptr));
156}
157
158// Safe access to native pointer from object. Throws if not possible to access.
159static CameraMetadata* CameraMetadata_getPointerThrow(JNIEnv *env, jobject thiz,
160                                                 const char* argName = "this") {
161
162    if (thiz == NULL) {
163        ALOGV("%s: Throwing java.lang.NullPointerException for null reference",
164              __FUNCTION__);
165        jniThrowNullPointerException(env, argName);
166        return NULL;
167    }
168
169    CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
170    if (metadata == NULL) {
171        ALOGV("%s: Throwing java.lang.IllegalStateException for closed object",
172              __FUNCTION__);
173        jniThrowException(env, "java/lang/IllegalStateException",
174                            "Metadata object was already closed");
175        return NULL;
176    }
177
178    return metadata;
179}
180
181static jlong CameraMetadata_allocate(JNIEnv *env, jobject thiz) {
182    ALOGV("%s", __FUNCTION__);
183
184    return reinterpret_cast<jlong>(new CameraMetadata());
185}
186
187static jlong CameraMetadata_allocateCopy(JNIEnv *env, jobject thiz,
188        jobject other) {
189    ALOGV("%s", __FUNCTION__);
190
191    CameraMetadata* otherMetadata =
192            CameraMetadata_getPointerThrow(env, other, "other");
193
194    // In case of exception, return
195    if (otherMetadata == NULL) return NULL;
196
197    // Clone native metadata and return new pointer
198    return reinterpret_cast<jlong>(new CameraMetadata(*otherMetadata));
199}
200
201
202static jboolean CameraMetadata_isEmpty(JNIEnv *env, jobject thiz) {
203    ALOGV("%s", __FUNCTION__);
204
205    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
206
207    if (metadata == NULL) {
208        ALOGW("%s: Returning early due to exception being thrown",
209               __FUNCTION__);
210        return JNI_TRUE; // actually throws java exc.
211    }
212
213    jboolean empty = metadata->isEmpty();
214
215    ALOGV("%s: Empty returned %d, entry count was %zu",
216          __FUNCTION__, empty, metadata->entryCount());
217
218    return empty;
219}
220
221static jint CameraMetadata_getEntryCount(JNIEnv *env, jobject thiz) {
222    ALOGV("%s", __FUNCTION__);
223
224    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
225
226    if (metadata == NULL) return 0; // actually throws java exc.
227
228    return metadata->entryCount();
229}
230
231// idempotent. calling more than once has no effect.
232static void CameraMetadata_close(JNIEnv *env, jobject thiz) {
233    ALOGV("%s", __FUNCTION__);
234
235    CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
236
237    if (metadata != NULL) {
238        delete metadata;
239        env->SetLongField(thiz, fields.metadata_ptr, 0);
240    }
241
242    LOG_ALWAYS_FATAL_IF(CameraMetadata_getPointerNoThrow(env, thiz) != NULL,
243                        "Expected the native ptr to be 0 after #close");
244}
245
246static void CameraMetadata_swap(JNIEnv *env, jobject thiz, jobject other) {
247    ALOGV("%s", __FUNCTION__);
248
249    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
250
251    // order is important: we can't call another JNI method
252    // if there is an exception pending
253    if (metadata == NULL) return;
254
255    CameraMetadata* otherMetadata = CameraMetadata_getPointerThrow(env, other, "other");
256
257    if (otherMetadata == NULL) return;
258
259    metadata->swap(*otherMetadata);
260}
261
262static jbyteArray CameraMetadata_readValues(JNIEnv *env, jobject thiz, jint tag) {
263    ALOGV("%s (tag = %d)", __FUNCTION__, tag);
264
265    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
266    if (metadata == NULL) return NULL;
267
268    int tagType = get_camera_metadata_tag_type(tag);
269    if (tagType == -1) {
270        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
271                             "Tag (%d) did not have a type", tag);
272        return NULL;
273    }
274    size_t tagSize = Helpers::getTypeSize(tagType);
275
276    camera_metadata_entry entry = metadata->find(tag);
277    if (entry.count == 0) {
278         if (!metadata->exists(tag)) {
279             ALOGV("%s: Tag %d does not have any entries", __FUNCTION__, tag);
280             return NULL;
281         } else {
282             // OK: we will return a 0-sized array.
283             ALOGV("%s: Tag %d had an entry, but it had 0 data", __FUNCTION__,
284                   tag);
285         }
286    }
287
288    jsize byteCount = entry.count * tagSize;
289    jbyteArray byteArray = env->NewByteArray(byteCount);
290    if (env->ExceptionCheck()) return NULL;
291
292    // Copy into java array from native array
293    ScopedByteArrayRW arrayWriter(env, byteArray);
294    memcpy(arrayWriter.get(), entry.data.u8, byteCount);
295
296    return byteArray;
297}
298
299static void CameraMetadata_writeValues(JNIEnv *env, jobject thiz, jint tag, jbyteArray src) {
300    ALOGV("%s (tag = %d)", __FUNCTION__, tag);
301
302    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
303    if (metadata == NULL) return;
304
305    int tagType = get_camera_metadata_tag_type(tag);
306    if (tagType == -1) {
307        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
308                             "Tag (%d) did not have a type", tag);
309        return;
310    }
311
312    status_t res;
313
314    if (src == NULL) {
315        // If array is NULL, delete the entry
316        if (metadata->exists(tag)) {
317            res = metadata->erase(tag);
318            ALOGV("%s: Erase values (res = %d)", __FUNCTION__, res);
319        } else {
320            res = OK;
321            ALOGV("%s: Don't need to erase", __FUNCTION__);
322        }
323    } else {
324        // Copy from java array into native array
325        ScopedByteArrayRO arrayReader(env, src);
326        if (arrayReader.get() == NULL) return;
327
328        res = Helpers::updateAny(metadata, static_cast<uint32_t>(tag),
329                                 tagType, arrayReader.get(), arrayReader.size());
330
331        ALOGV("%s: Update values (res = %d)", __FUNCTION__, res);
332    }
333
334    if (res == OK) {
335        return;
336    } else if (res == BAD_VALUE) {
337        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
338                             "Src byte array was poorly formed");
339    } else if (res == INVALID_OPERATION) {
340        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
341                             "Internal error while trying to update metadata");
342    } else {
343        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
344                             "Unknown error (%d) while trying to update "
345                            "metadata", res);
346    }
347}
348
349struct DumpMetadataParams {
350    int writeFd;
351    const CameraMetadata* metadata;
352};
353
354static void* CameraMetadata_writeMetadataThread(void* arg) {
355    DumpMetadataParams* p = static_cast<DumpMetadataParams*>(arg);
356
357    /*
358     * Write the dumped data, and close the writing side FD.
359     */
360    p->metadata->dump(p->writeFd, /*verbosity*/2);
361
362    if (close(p->writeFd) < 0) {
363        ALOGE("%s: Failed to close writeFd (errno = %#x, message = '%s')",
364                __FUNCTION__, errno, strerror(errno));
365    }
366
367    return NULL;
368}
369
370static void CameraMetadata_dump(JNIEnv *env, jobject thiz) {
371    ALOGV("%s", __FUNCTION__);
372    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
373    if (metadata == NULL) {
374        return;
375    }
376
377    /*
378     * Create a socket pair for local streaming read/writes.
379     *
380     * The metadata will be dumped into the write side,
381     * and then read back out (and logged) via the read side.
382     */
383
384    int writeFd, readFd;
385    {
386
387        int sv[2];
388        if (socketpair(AF_LOCAL, SOCK_STREAM, /*protocol*/0, &sv[0]) < 0) {
389            jniThrowExceptionFmt(env, "java/io/IOException",
390                    "Failed to create socketpair (errno = %#x, message = '%s')",
391                    errno, strerror(errno));
392            return;
393        }
394        writeFd = sv[0];
395        readFd = sv[1];
396    }
397
398    /*
399     * Create a thread for doing the writing.
400     *
401     * The reading and writing must be concurrent, otherwise
402     * the write will block forever once it exhausts the capped
403     * buffer size (from getsockopt).
404     */
405    pthread_t writeThread;
406    DumpMetadataParams params = {
407        writeFd,
408        metadata
409    };
410
411    {
412        int threadRet = pthread_create(&writeThread, /*attr*/NULL,
413                CameraMetadata_writeMetadataThread, (void*)&params);
414
415        if (threadRet != 0) {
416            close(writeFd);
417
418            jniThrowExceptionFmt(env, "java/io/IOException",
419                    "Failed to create thread for writing (errno = %#x, message = '%s')",
420                    threadRet, strerror(threadRet));
421        }
422    }
423
424    /*
425     * Read out a byte until stream is complete. Write completed lines
426     * to ALOG.
427     */
428    {
429        char out[] = {'\0', '\0'}; // large enough to append as a string
430        String8 logLine;
431
432        // Read one byte at a time! Very slow but avoids complicated \n scanning.
433        ssize_t res;
434        while ((res = TEMP_FAILURE_RETRY(read(readFd, &out[0], /*count*/1))) > 0) {
435            if (out[0] == '\n') {
436                ALOGD("%s", logLine.string());
437                logLine.clear();
438            } else {
439                logLine.append(out);
440            }
441        }
442
443        if (res < 0) {
444            jniThrowExceptionFmt(env, "java/io/IOException",
445                    "Failed to read from fd (errno = %#x, message = '%s')",
446                    errno, strerror(errno));
447            //return;
448        } else if (!logLine.isEmpty()) {
449            ALOGD("%s", logLine.string());
450        }
451    }
452
453    int res;
454
455    // Join until thread finishes. Ensures params/metadata is valid until then.
456    if ((res = pthread_join(writeThread, /*retval*/NULL)) != 0) {
457        ALOGE("%s: Failed to join thread (errno = %#x, message = '%s')",
458                __FUNCTION__, res, strerror(res));
459    }
460}
461
462static void CameraMetadata_readFromParcel(JNIEnv *env, jobject thiz, jobject parcel) {
463    ALOGV("%s", __FUNCTION__);
464    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
465    if (metadata == NULL) {
466        return;
467    }
468
469    Parcel* parcelNative = parcelForJavaObject(env, parcel);
470    if (parcelNative == NULL) {
471        jniThrowNullPointerException(env, "parcel");
472        return;
473    }
474
475    status_t err;
476    if ((err = metadata->readFromParcel(parcelNative)) != OK) {
477        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
478                             "Failed to read from parcel (error code %d)", err);
479        return;
480    }
481}
482
483static void CameraMetadata_writeToParcel(JNIEnv *env, jobject thiz, jobject parcel) {
484    ALOGV("%s", __FUNCTION__);
485    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
486    if (metadata == NULL) {
487        return;
488    }
489
490    Parcel* parcelNative = parcelForJavaObject(env, parcel);
491    if (parcelNative == NULL) {
492        jniThrowNullPointerException(env, "parcel");
493        return;
494    }
495
496    status_t err;
497    if ((err = metadata->writeToParcel(parcelNative)) != OK) {
498        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
499                                  "Failed to write to parcel (error code %d)", err);
500        return;
501    }
502}
503
504} // extern "C"
505
506//-------------------------------------------------
507
508static const JNINativeMethod gCameraMetadataMethods[] = {
509// static methods
510  { "nativeClassInit",
511    "()V",
512    (void *)CameraMetadata_classInit },
513  { "nativeGetTagFromKey",
514    "(Ljava/lang/String;)I",
515    (void *)CameraMetadata_getTagFromKey },
516  { "nativeGetTypeFromTag",
517    "(I)I",
518    (void *)CameraMetadata_getTypeFromTag },
519  { "nativeSetupGlobalVendorTagDescriptor",
520    "()I",
521    (void*)CameraMetadata_setupGlobalVendorTagDescriptor },
522// instance methods
523  { "nativeAllocate",
524    "()J",
525    (void*)CameraMetadata_allocate },
526  { "nativeAllocateCopy",
527    "(L" CAMERA_METADATA_CLASS_NAME ";)J",
528    (void *)CameraMetadata_allocateCopy },
529  { "nativeIsEmpty",
530    "()Z",
531    (void*)CameraMetadata_isEmpty },
532  { "nativeGetEntryCount",
533    "()I",
534    (void*)CameraMetadata_getEntryCount },
535  { "nativeClose",
536    "()V",
537    (void*)CameraMetadata_close },
538  { "nativeSwap",
539    "(L" CAMERA_METADATA_CLASS_NAME ";)V",
540    (void *)CameraMetadata_swap },
541  { "nativeReadValues",
542    "(I)[B",
543    (void *)CameraMetadata_readValues },
544  { "nativeWriteValues",
545    "(I[B)V",
546    (void *)CameraMetadata_writeValues },
547  { "nativeDump",
548    "()V",
549    (void *)CameraMetadata_dump },
550// Parcelable interface
551  { "nativeReadFromParcel",
552    "(Landroid/os/Parcel;)V",
553    (void *)CameraMetadata_readFromParcel },
554  { "nativeWriteToParcel",
555    "(Landroid/os/Parcel;)V",
556    (void *)CameraMetadata_writeToParcel },
557};
558
559struct field {
560    const char *class_name;
561    const char *field_name;
562    const char *field_type;
563    jfieldID   *jfield;
564};
565
566static int find_fields(JNIEnv *env, field *fields, int count)
567{
568    for (int i = 0; i < count; i++) {
569        field *f = &fields[i];
570        jclass clazz = env->FindClass(f->class_name);
571        if (clazz == NULL) {
572            ALOGE("Can't find %s", f->class_name);
573            return -1;
574        }
575
576        jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type);
577        if (field == NULL) {
578            ALOGE("Can't find %s.%s", f->class_name, f->field_name);
579            return -1;
580        }
581
582        *(f->jfield) = field;
583    }
584
585    return 0;
586}
587
588// Get all the required offsets in java class and register native functions
589int register_android_hardware_camera2_CameraMetadata(JNIEnv *env)
590{
591    // Register native functions
592    return RegisterMethodsOrDie(env,
593            CAMERA_METADATA_CLASS_NAME,
594            gCameraMetadataMethods,
595            NELEM(gCameraMetadataMethods));
596}
597
598extern "C" {
599static void CameraMetadata_classInit(JNIEnv *env, jobject thiz) {
600    // XX: Why do this separately instead of doing it in the register function?
601    ALOGV("%s", __FUNCTION__);
602
603    field fields_to_find[] = {
604        { CAMERA_METADATA_CLASS_NAME, "mMetadataPtr", "J", &fields.metadata_ptr },
605    };
606
607    // Do this here instead of in register_native_methods,
608    // since otherwise it will fail to find the fields.
609    if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0)
610        return;
611
612    env->FindClass(CAMERA_METADATA_CLASS_NAME);
613}
614
615static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName) {
616
617    ScopedUtfChars keyScoped(env, keyName);
618    const char *key = keyScoped.c_str();
619    if (key == NULL) {
620        // exception thrown by ScopedUtfChars
621        return 0;
622    }
623    size_t keyLength = strlen(key);
624
625    ALOGV("%s (key = '%s')", __FUNCTION__, key);
626
627    sp<VendorTagDescriptor> vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor();
628
629    SortedVector<String8> vendorSections;
630    size_t vendorSectionCount = 0;
631
632    if (vTags != NULL) {
633        vendorSections = vTags->getAllSectionNames();
634        vendorSectionCount = vendorSections.size();
635    }
636
637    // First, find the section by the longest string match
638    const char *section = NULL;
639    size_t sectionIndex = 0;
640    size_t sectionLength = 0;
641    size_t totalSectionCount = ANDROID_SECTION_COUNT + vendorSectionCount;
642    for (size_t i = 0; i < totalSectionCount; ++i) {
643
644        const char *str = (i < ANDROID_SECTION_COUNT) ? camera_metadata_section_names[i] :
645                vendorSections[i - ANDROID_SECTION_COUNT].string();
646        if (kIsDebug) {
647            ALOGV("%s: Trying to match against section '%s'", __FUNCTION__, str);
648        }
649        if (strstr(key, str) == key) { // key begins with the section name
650            size_t strLength = strlen(str);
651
652            if (kIsDebug) {
653                ALOGV("%s: Key begins with section name", __FUNCTION__);
654            }
655
656            // section name is the longest we've found so far
657            if (section == NULL || sectionLength < strLength) {
658                section = str;
659                sectionIndex = i;
660                sectionLength = strLength;
661
662                if (kIsDebug) {
663                    ALOGV("%s: Found new best section (%s)", __FUNCTION__, section);
664                }
665            }
666        }
667    }
668
669    // TODO: Make above get_camera_metadata_section_from_name ?
670
671    if (section == NULL) {
672        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
673                             "Could not find section name for key '%s')", key);
674        return 0;
675    } else {
676        ALOGV("%s: Found matched section '%s' (%zu)",
677              __FUNCTION__, section, sectionIndex);
678    }
679
680    // Get the tag name component of the key
681    const char *keyTagName = key + sectionLength + 1; // x.y.z -> z
682    if (sectionLength + 1 >= keyLength) {
683        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
684                             "Key length too short for key '%s')", key);
685        return 0;
686    }
687
688    // Match rest of name against the tag names in that section only
689    uint32_t tag = 0;
690    if (sectionIndex < ANDROID_SECTION_COUNT) {
691        // Match built-in tags (typically android.*)
692        uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd)
693        tagBegin = camera_metadata_section_bounds[sectionIndex][0];
694        tagEnd = camera_metadata_section_bounds[sectionIndex][1];
695
696        for (tag = tagBegin; tag < tagEnd; ++tag) {
697            const char *tagName = get_camera_metadata_tag_name(tag);
698
699            if (strcmp(keyTagName, tagName) == 0) {
700                ALOGV("%s: Found matched tag '%s' (%d)",
701                      __FUNCTION__, tagName, tag);
702                break;
703            }
704        }
705
706        if (tag == tagEnd) {
707            jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
708                                 "Could not find tag name for key '%s')", key);
709            return 0;
710        }
711    } else if (vTags != NULL) {
712        // Match vendor tags (typically com.*)
713        const String8 sectionName(section);
714        const String8 tagName(keyTagName);
715
716        status_t res = OK;
717        if ((res = vTags->lookupTag(tagName, sectionName, &tag)) != OK) {
718            jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
719                    "%s: No vendor tag matches key '%s'", __FUNCTION__, key);
720            return 0;
721        }
722    }
723
724    // TODO: Make above get_camera_metadata_tag_from_name ?
725
726    return tag;
727}
728
729static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag) {
730    int tagType = get_camera_metadata_tag_type(tag);
731    if (tagType == -1) {
732        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
733                             "Tag (%d) did not have a type", tag);
734        return -1;
735    }
736
737    return tagType;
738}
739
740static jint CameraMetadata_setupGlobalVendorTagDescriptor(JNIEnv *env, jobject thiz) {
741    const String16 NAME("media.camera");
742    sp<ICameraService> cameraService;
743    status_t err = getService(NAME, /*out*/&cameraService);
744
745    if (err != OK) {
746        ALOGE("%s: Failed to get camera service, received error %s (%d)", __FUNCTION__,
747                strerror(-err), err);
748        return err;
749    }
750
751    sp<VendorTagDescriptor> desc;
752    err = cameraService->getCameraVendorTagDescriptor(/*out*/desc);
753
754    if (err == -EOPNOTSUPP) {
755        ALOGW("%s: Camera HAL too old; does not support vendor tags", __FUNCTION__);
756        VendorTagDescriptor::clearGlobalVendorTagDescriptor();
757
758        return OK;
759    } else if (err != OK) {
760        ALOGE("%s: Failed to setup vendor tag descriptors, received error %s (%d)",
761                __FUNCTION__, strerror(-err), err);
762        return err;
763    }
764
765    err = VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc);
766
767    return err;
768}
769
770} // extern "C"
771