android_hardware_camera2_CameraMetadata.cpp revision 70c2207c34cf0e6b3b383b1b1500ff5385aa51a6
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/Log.h>
22
23#include "jni.h"
24#include "JNIHelp.h"
25#include "android_os_Parcel.h"
26#include "android_runtime/AndroidRuntime.h"
27
28#include <camera/CameraMetadata.h>
29#include <nativehelper/ScopedUtfChars.h>
30#include <nativehelper/ScopedPrimitiveArray.h>
31
32#if defined(LOG_NNDEBUG)
33#if !LOG_NNDEBUG
34#define ALOGVV ALOGV
35#endif
36#else
37#define ALOGVV(...)
38#endif
39
40// fully-qualified class name
41#define CAMERA_METADATA_CLASS_NAME "android/hardware/camera2/impl/CameraMetadataNative"
42
43using namespace android;
44
45struct fields_t {
46    jfieldID    metadata_ptr;
47};
48
49static fields_t fields;
50
51namespace {
52struct Helpers {
53    static size_t getTypeSize(uint8_t type) {
54        if (type >= NUM_TYPES) {
55            ALOGE("%s: Invalid type specified (%ud)", __FUNCTION__, type);
56            return static_cast<size_t>(-1);
57        }
58
59        return camera_metadata_type_size[type];
60    }
61
62    static status_t updateAny(CameraMetadata *metadata,
63                          uint32_t tag,
64                          uint32_t type,
65                          const void *data,
66                          size_t dataBytes) {
67
68        if (type >= NUM_TYPES) {
69            ALOGE("%s: Invalid type specified (%ud)", __FUNCTION__, type);
70            return INVALID_OPERATION;
71        }
72
73        size_t typeSize = getTypeSize(type);
74
75        if (dataBytes % typeSize != 0) {
76            ALOGE("%s: Expected dataBytes (%ud) to be divisible by typeSize "
77                  "(%ud)", __FUNCTION__, dataBytes, typeSize);
78            return BAD_VALUE;
79        }
80
81        size_t dataCount = dataBytes / typeSize;
82
83        switch(type) {
84#define METADATA_UPDATE(runtime_type, compile_type)                            \
85            case runtime_type: {                                               \
86                const compile_type *dataPtr =                                  \
87                        static_cast<const compile_type*>(data);                \
88                return metadata->update(tag, dataPtr, dataCount);              \
89            }                                                                  \
90
91            METADATA_UPDATE(TYPE_BYTE,     uint8_t);
92            METADATA_UPDATE(TYPE_INT32,    int32_t);
93            METADATA_UPDATE(TYPE_FLOAT,    float);
94            METADATA_UPDATE(TYPE_INT64,    int64_t);
95            METADATA_UPDATE(TYPE_DOUBLE,   double);
96            METADATA_UPDATE(TYPE_RATIONAL, camera_metadata_rational_t);
97
98            default: {
99                // unreachable
100                ALOGE("%s: Unreachable", __FUNCTION__);
101                return INVALID_OPERATION;
102            }
103        }
104
105#undef METADATA_UPDATE
106    }
107};
108} // namespace {}
109
110extern "C" {
111
112static void CameraMetadata_classInit(JNIEnv *env, jobject thiz);
113static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName);
114static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag);
115
116// Less safe access to native pointer. Does NOT throw any Java exceptions if NULL.
117static CameraMetadata* CameraMetadata_getPointerNoThrow(JNIEnv *env, jobject thiz) {
118
119    if (thiz == NULL) {
120        return NULL;
121    }
122
123    return reinterpret_cast<CameraMetadata*>(env->GetLongField(thiz, fields.metadata_ptr));
124}
125
126// Safe access to native pointer from object. Throws if not possible to access.
127static CameraMetadata* CameraMetadata_getPointerThrow(JNIEnv *env, jobject thiz,
128                                                 const char* argName = "this") {
129
130    if (thiz == NULL) {
131        ALOGV("%s: Throwing java.lang.NullPointerException for null reference",
132              __FUNCTION__);
133        jniThrowNullPointerException(env, argName);
134        return NULL;
135    }
136
137    CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
138    if (metadata == NULL) {
139        ALOGV("%s: Throwing java.lang.IllegalStateException for closed object",
140              __FUNCTION__);
141        jniThrowException(env, "java/lang/IllegalStateException",
142                            "Metadata object was already closed");
143        return NULL;
144    }
145
146    return metadata;
147}
148
149static jlong CameraMetadata_allocate(JNIEnv *env, jobject thiz) {
150    ALOGV("%s", __FUNCTION__);
151
152    return reinterpret_cast<jlong>(new CameraMetadata());
153}
154
155static jlong CameraMetadata_allocateCopy(JNIEnv *env, jobject thiz,
156        jobject other) {
157    ALOGV("%s", __FUNCTION__);
158
159    CameraMetadata* otherMetadata =
160            CameraMetadata_getPointerThrow(env, other, "other");
161
162    // In case of exception, return
163    if (otherMetadata == NULL) return NULL;
164
165    // Clone native metadata and return new pointer
166    return reinterpret_cast<jlong>(new CameraMetadata(*otherMetadata));
167}
168
169
170static jboolean CameraMetadata_isEmpty(JNIEnv *env, jobject thiz) {
171    ALOGV("%s", __FUNCTION__);
172
173    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
174
175    if (metadata == NULL) {
176        ALOGW("%s: Returning early due to exception being thrown",
177               __FUNCTION__);
178        return JNI_TRUE; // actually throws java exc.
179    }
180
181    jboolean empty = metadata->isEmpty();
182
183    ALOGV("%s: Empty returned %d, entry count was %d",
184          __FUNCTION__, empty, metadata->entryCount());
185
186    return empty;
187}
188
189static jint CameraMetadata_getEntryCount(JNIEnv *env, jobject thiz) {
190    ALOGV("%s", __FUNCTION__);
191
192    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
193
194    if (metadata == NULL) return 0; // actually throws java exc.
195
196    return metadata->entryCount();
197}
198
199// idempotent. calling more than once has no effect.
200static void CameraMetadata_close(JNIEnv *env, jobject thiz) {
201    ALOGV("%s", __FUNCTION__);
202
203    CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
204
205    if (metadata != NULL) {
206        delete metadata;
207        env->SetLongField(thiz, fields.metadata_ptr, 0);
208    }
209
210    LOG_ALWAYS_FATAL_IF(CameraMetadata_getPointerNoThrow(env, thiz) != NULL,
211                        "Expected the native ptr to be 0 after #close");
212}
213
214static void CameraMetadata_swap(JNIEnv *env, jobject thiz, jobject other) {
215    ALOGV("%s", __FUNCTION__);
216
217    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
218
219    // order is important: we can't call another JNI method
220    // if there is an exception pending
221    if (metadata == NULL) return;
222
223    CameraMetadata* otherMetadata = CameraMetadata_getPointerThrow(env, other, "other");
224
225    if (otherMetadata == NULL) return;
226
227    metadata->swap(*otherMetadata);
228}
229
230static jbyteArray CameraMetadata_readValues(JNIEnv *env, jobject thiz, jint tag) {
231    ALOGV("%s (tag = %d)", __FUNCTION__, tag);
232
233    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
234    if (metadata == NULL) return NULL;
235
236    int tagType = get_camera_metadata_tag_type(tag);
237    if (tagType == -1) {
238        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
239                             "Tag (%d) did not have a type", tag);
240        return NULL;
241    }
242    size_t tagSize = Helpers::getTypeSize(tagType);
243
244    camera_metadata_entry entry = metadata->find(tag);
245    if (entry.count == 0) {
246         if (!metadata->exists(tag)) {
247             ALOGV("%s: Tag %d does not have any entries", __FUNCTION__, tag);
248             return NULL;
249         } else {
250             // OK: we will return a 0-sized array.
251             ALOGV("%s: Tag %d had an entry, but it had 0 data", __FUNCTION__,
252                   tag);
253         }
254    }
255
256    jsize byteCount = entry.count * tagSize;
257    jbyteArray byteArray = env->NewByteArray(byteCount);
258    if (env->ExceptionCheck()) return NULL;
259
260    // Copy into java array from native array
261    ScopedByteArrayRW arrayWriter(env, byteArray);
262    memcpy(arrayWriter.get(), entry.data.u8, byteCount);
263
264    return byteArray;
265}
266
267static void CameraMetadata_writeValues(JNIEnv *env, jobject thiz, jint tag, jbyteArray src) {
268    ALOGV("%s (tag = %d)", __FUNCTION__, tag);
269
270    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
271    if (metadata == NULL) return;
272
273    int tagType = get_camera_metadata_tag_type(tag);
274    if (tagType == -1) {
275        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
276                             "Tag (%d) did not have a type", tag);
277        return;
278    }
279    size_t tagSize = Helpers::getTypeSize(tagType);
280
281    status_t res;
282
283    if (src == NULL) {
284        // If array is NULL, delete the entry
285        if (metadata->exists(tag)) {
286            res = metadata->erase(tag);
287            ALOGV("%s: Erase values (res = %d)", __FUNCTION__, res);
288        } else {
289            res = OK;
290            ALOGV("%s: Don't need to erase", __FUNCTION__);
291        }
292    } else {
293        // Copy from java array into native array
294        ScopedByteArrayRO arrayReader(env, src);
295        if (arrayReader.get() == NULL) return;
296
297        res = Helpers::updateAny(metadata, static_cast<uint32_t>(tag),
298                                 tagType, arrayReader.get(), arrayReader.size());
299
300        ALOGV("%s: Update values (res = %d)", __FUNCTION__, res);
301    }
302
303    if (res == OK) {
304        return;
305    } else if (res == BAD_VALUE) {
306        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
307                             "Src byte array was poorly formed");
308    } else if (res == INVALID_OPERATION) {
309        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
310                             "Internal error while trying to update metadata");
311    } else {
312        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
313                             "Unknown error (%d) while trying to update "
314                            "metadata", res);
315    }
316}
317
318static void CameraMetadata_readFromParcel(JNIEnv *env, jobject thiz, jobject parcel) {
319    ALOGV("%s", __FUNCTION__);
320    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
321    if (metadata == NULL) {
322        return;
323    }
324
325    Parcel* parcelNative = parcelForJavaObject(env, parcel);
326    if (parcelNative == NULL) {
327        jniThrowNullPointerException(env, "parcel");
328        return;
329    }
330
331    status_t err;
332    if ((err = metadata->readFromParcel(parcelNative)) != OK) {
333        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
334                             "Failed to read from parcel (error code %d)", err);
335        return;
336    }
337}
338
339static void CameraMetadata_writeToParcel(JNIEnv *env, jobject thiz, jobject parcel) {
340    ALOGV("%s", __FUNCTION__);
341    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
342    if (metadata == NULL) {
343        return;
344    }
345
346    Parcel* parcelNative = parcelForJavaObject(env, parcel);
347    if (parcelNative == NULL) {
348        jniThrowNullPointerException(env, "parcel");
349        return;
350    }
351
352    status_t err;
353    if ((err = metadata->writeToParcel(parcelNative)) != OK) {
354        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
355                                  "Failed to write to parcel (error code %d)", err);
356        return;
357    }
358}
359
360} // extern "C"
361
362//-------------------------------------------------
363
364static JNINativeMethod gCameraMetadataMethods[] = {
365// static methods
366  { "nativeClassInit",
367    "()V",
368    (void *)CameraMetadata_classInit },
369  { "nativeGetTagFromKey",
370    "(Ljava/lang/String;)I",
371    (void *)CameraMetadata_getTagFromKey },
372  { "nativeGetTypeFromTag",
373    "(I)I",
374    (void *)CameraMetadata_getTypeFromTag },
375// instance methods
376  { "nativeAllocate",
377    "()J",
378    (void*)CameraMetadata_allocate },
379  { "nativeAllocateCopy",
380    "(L" CAMERA_METADATA_CLASS_NAME ";)J",
381    (void *)CameraMetadata_allocateCopy },
382  { "nativeIsEmpty",
383    "()Z",
384    (void*)CameraMetadata_isEmpty },
385  { "nativeGetEntryCount",
386    "()I",
387    (void*)CameraMetadata_getEntryCount },
388  { "nativeClose",
389    "()V",
390    (void*)CameraMetadata_close },
391  { "nativeSwap",
392    "(L" CAMERA_METADATA_CLASS_NAME ";)V",
393    (void *)CameraMetadata_swap },
394  { "nativeReadValues",
395    "(I)[B",
396    (void *)CameraMetadata_readValues },
397  { "nativeWriteValues",
398    "(I[B)V",
399    (void *)CameraMetadata_writeValues },
400// Parcelable interface
401  { "nativeReadFromParcel",
402    "(Landroid/os/Parcel;)V",
403    (void *)CameraMetadata_readFromParcel },
404  { "nativeWriteToParcel",
405    "(Landroid/os/Parcel;)V",
406    (void *)CameraMetadata_writeToParcel },
407};
408
409struct field {
410    const char *class_name;
411    const char *field_name;
412    const char *field_type;
413    jfieldID   *jfield;
414};
415
416static int find_fields(JNIEnv *env, field *fields, int count)
417{
418    for (int i = 0; i < count; i++) {
419        field *f = &fields[i];
420        jclass clazz = env->FindClass(f->class_name);
421        if (clazz == NULL) {
422            ALOGE("Can't find %s", f->class_name);
423            return -1;
424        }
425
426        jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type);
427        if (field == NULL) {
428            ALOGE("Can't find %s.%s", f->class_name, f->field_name);
429            return -1;
430        }
431
432        *(f->jfield) = field;
433    }
434
435    return 0;
436}
437
438// Get all the required offsets in java class and register native functions
439int register_android_hardware_camera2_CameraMetadata(JNIEnv *env)
440{
441    // Register native functions
442    return AndroidRuntime::registerNativeMethods(env,
443            CAMERA_METADATA_CLASS_NAME,
444            gCameraMetadataMethods,
445            NELEM(gCameraMetadataMethods));
446}
447
448extern "C" {
449static void CameraMetadata_classInit(JNIEnv *env, jobject thiz) {
450    // XX: Why do this separately instead of doing it in the register function?
451    ALOGV("%s", __FUNCTION__);
452
453    field fields_to_find[] = {
454        { CAMERA_METADATA_CLASS_NAME, "mMetadataPtr", "J", &fields.metadata_ptr },
455    };
456
457    // Do this here instead of in register_native_methods,
458    // since otherwise it will fail to find the fields.
459    if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0)
460        return;
461
462    jclass clazz = env->FindClass(CAMERA_METADATA_CLASS_NAME);
463}
464
465static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName) {
466
467    ScopedUtfChars keyScoped(env, keyName);
468    const char *key = keyScoped.c_str();
469    if (key == NULL) {
470        // exception thrown by ScopedUtfChars
471        return 0;
472    }
473    size_t keyLength = strlen(key);
474
475    ALOGV("%s (key = '%s')", __FUNCTION__, key);
476
477    // First, find the section by the longest string match
478    const char *section = NULL;
479    size_t sectionIndex = 0;
480    size_t sectionLength = 0;
481    for (size_t i = 0; i < ANDROID_SECTION_COUNT; ++i) {
482        const char *str = camera_metadata_section_names[i];
483        ALOGVV("%s: Trying to match against section '%s'",
484               __FUNCTION__, str);
485        if (strstr(key, str) == key) { // key begins with the section name
486            size_t strLength = strlen(str);
487
488            ALOGVV("%s: Key begins with section name", __FUNCTION__);
489
490            // section name is the longest we've found so far
491            if (section == NULL || sectionLength < strLength) {
492                section = str;
493                sectionIndex = i;
494                sectionLength = strLength;
495
496                ALOGVV("%s: Found new best section (idx %d)", __FUNCTION__, sectionIndex);
497            }
498        }
499    }
500
501    // TODO: vendor ext
502    // TODO: Make above get_camera_metadata_section_from_name ?
503
504    if (section == NULL) {
505        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
506                             "Could not find section name for key '%s')", key);
507        return 0;
508    } else {
509        ALOGV("%s: Found matched section '%s' (%d)",
510              __FUNCTION__, section, sectionIndex);
511    }
512
513    // Get the tag name component of the key
514    const char *keyTagName = key + sectionLength + 1; // x.y.z -> z
515    if (sectionLength + 1 >= keyLength) {
516        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
517                             "Key length too short for key '%s')", key);
518    }
519
520    // Match rest of name against the tag names in that section only
521    uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd)
522    tagBegin = camera_metadata_section_bounds[sectionIndex][0];
523    tagEnd = camera_metadata_section_bounds[sectionIndex][1];
524
525    uint32_t tag;
526    for (tag = tagBegin; tag < tagEnd; ++tag) {
527        const char *tagName = get_camera_metadata_tag_name(tag);
528
529        if (strcmp(keyTagName, tagName) == 0) {
530            ALOGV("%s: Found matched tag '%s' (%d)",
531                  __FUNCTION__, tagName, tag);
532            break;
533        }
534    }
535
536    // TODO: vendor ext
537    // TODO: Make above get_camera_metadata_tag_from_name ?
538
539    if (tag == tagEnd) {
540        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
541                             "Could not find tag name for key '%s')", key);
542        return 0;
543    }
544
545    return tag;
546}
547
548static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag) {
549    int tagType = get_camera_metadata_tag_type(tag);
550    if (tagType == -1) {
551        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
552                             "Tag (%d) did not have a type", tag);
553        return -1;
554    }
555
556    return tagType;
557}
558
559} // extern "C"
560