blob: 3c7da1e80b2b84a5ef4d464ef8e98469767cbf18 [file] [log] [blame]
Igor Murashkin70725502013-06-25 20:27:06 +00001/*
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
Igor Murashkinb519cc52013-07-02 11:23:44 -070019// #define LOG_NNDEBUG 0
Igor Murashkin70725502013-06-25 20:27:06 +000020#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>
Igor Murashkinb519cc52013-07-02 11:23:44 -070029#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
Igor Murashkin70725502013-06-25 20:27:06 +000039
40// fully-qualified class name
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070041#define CAMERA_METADATA_CLASS_NAME "android/hardware/camera2/impl/CameraMetadataNative"
Igor Murashkin70725502013-06-25 20:27:06 +000042
43using namespace android;
44
45struct fields_t {
46 jfieldID metadata_ptr;
47};
48
49static fields_t fields;
50
Igor Murashkinb519cc52013-07-02 11:23:44 -070051namespace {
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
Igor Murashkin70725502013-06-25 20:27:06 +0000110extern "C" {
111
112static void CameraMetadata_classInit(JNIEnv *env, jobject thiz);
Igor Murashkinb519cc52013-07-02 11:23:44 -0700113static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName);
114static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag);
Igor Murashkin70725502013-06-25 20:27:06 +0000115
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
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700155static 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
Igor Murashkin70725502013-06-25 20:27:06 +0000170static 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
Igor Murashkinb519cc52013-07-02 11:23:44 -0700230static 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
Igor Murashkin3710db82013-07-18 20:11:17 -0700285 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 }
Igor Murashkinb519cc52013-07-02 11:23:44 -0700292 } 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());
Igor Murashkin3710db82013-07-18 20:11:17 -0700299
300 ALOGV("%s: Update values (res = %d)", __FUNCTION__, res);
Igor Murashkinb519cc52013-07-02 11:23:44 -0700301 }
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
Igor Murashkin70725502013-06-25 20:27:06 +0000318static 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[] = {
Igor Murashkinb519cc52013-07-02 11:23:44 -0700365// static methods
Igor Murashkin70725502013-06-25 20:27:06 +0000366 { "nativeClassInit",
367 "()V",
368 (void *)CameraMetadata_classInit },
Igor Murashkinb519cc52013-07-02 11:23:44 -0700369 { "nativeGetTagFromKey",
370 "(Ljava/lang/String;)I",
371 (void *)CameraMetadata_getTagFromKey },
372 { "nativeGetTypeFromTag",
373 "(I)I",
374 (void *)CameraMetadata_getTypeFromTag },
375// instance methods
Igor Murashkin70725502013-06-25 20:27:06 +0000376 { "nativeAllocate",
377 "()J",
378 (void*)CameraMetadata_allocate },
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700379 { "nativeAllocateCopy",
380 "(L" CAMERA_METADATA_CLASS_NAME ";)J",
381 (void *)CameraMetadata_allocateCopy },
Igor Murashkin70725502013-06-25 20:27:06 +0000382 { "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 },
Igor Murashkinb519cc52013-07-02 11:23:44 -0700394 { "nativeReadValues",
395 "(I)[B",
396 (void *)CameraMetadata_readValues },
397 { "nativeWriteValues",
398 "(I[B)V",
399 (void *)CameraMetadata_writeValues },
400// Parcelable interface
Igor Murashkin70725502013-06-25 20:27:06 +0000401 { "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
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -0700439int register_android_hardware_camera2_CameraMetadata(JNIEnv *env)
Igor Murashkin70725502013-06-25 20:27:06 +0000440{
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}
Igor Murashkinb519cc52013-07-02 11:23:44 -0700464
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
Igor Murashkin70725502013-06-25 20:27:06 +0000559} // extern "C"