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