Home | History | Annotate | Download | only in jni
      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 
     43 using namespace android;
     44 
     45 struct fields_t {
     46     jfieldID    metadata_ptr;
     47 };
     48 
     49 static fields_t fields;
     50 
     51 namespace {
     52 struct 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 
    110 extern "C" {
    111 
    112 static void CameraMetadata_classInit(JNIEnv *env, jobject thiz);
    113 static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName);
    114 static 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.
    117 static 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.
    127 static 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 
    149 static jlong CameraMetadata_allocate(JNIEnv *env, jobject thiz) {
    150     ALOGV("%s", __FUNCTION__);
    151 
    152     return reinterpret_cast<jlong>(new CameraMetadata());
    153 }
    154 
    155 static 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 
    170 static 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 
    189 static 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.
    200 static 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 
    214 static 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 
    230 static 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 
    267 static 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 
    318 static 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 
    339 static 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 
    364 static 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 
    409 struct field {
    410     const char *class_name;
    411     const char *field_name;
    412     const char *field_type;
    413     jfieldID   *jfield;
    414 };
    415 
    416 static 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
    439 int 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 
    448 extern "C" {
    449 static 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 
    465 static 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 
    548 static 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