blob: 5070d2c8f4793e09735b72ca0b3a5d785c0e3a7a [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
41#define CAMERA_METADATA_CLASS_NAME "android/hardware/photography/CameraMetadata"
42
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
155static jboolean CameraMetadata_isEmpty(JNIEnv *env, jobject thiz) {
156 ALOGV("%s", __FUNCTION__);
157
158 CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
159
160 if (metadata == NULL) {
161 ALOGW("%s: Returning early due to exception being thrown",
162 __FUNCTION__);
163 return JNI_TRUE; // actually throws java exc.
164 }
165
166 jboolean empty = metadata->isEmpty();
167
168 ALOGV("%s: Empty returned %d, entry count was %d",
169 __FUNCTION__, empty, metadata->entryCount());
170
171 return empty;
172}
173
174static jint CameraMetadata_getEntryCount(JNIEnv *env, jobject thiz) {
175 ALOGV("%s", __FUNCTION__);
176
177 CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
178
179 if (metadata == NULL) return 0; // actually throws java exc.
180
181 return metadata->entryCount();
182}
183
184// idempotent. calling more than once has no effect.
185static void CameraMetadata_close(JNIEnv *env, jobject thiz) {
186 ALOGV("%s", __FUNCTION__);
187
188 CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
189
190 if (metadata != NULL) {
191 delete metadata;
192 env->SetLongField(thiz, fields.metadata_ptr, 0);
193 }
194
195 LOG_ALWAYS_FATAL_IF(CameraMetadata_getPointerNoThrow(env, thiz) != NULL,
196 "Expected the native ptr to be 0 after #close");
197}
198
199static void CameraMetadata_swap(JNIEnv *env, jobject thiz, jobject other) {
200 ALOGV("%s", __FUNCTION__);
201
202 CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
203
204 // order is important: we can't call another JNI method
205 // if there is an exception pending
206 if (metadata == NULL) return;
207
208 CameraMetadata* otherMetadata = CameraMetadata_getPointerThrow(env, other, "other");
209
210 if (otherMetadata == NULL) return;
211
212 metadata->swap(*otherMetadata);
213}
214
Igor Murashkinb519cc52013-07-02 11:23:44 -0700215static jbyteArray CameraMetadata_readValues(JNIEnv *env, jobject thiz, jint tag) {
216 ALOGV("%s (tag = %d)", __FUNCTION__, tag);
217
218 CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
219 if (metadata == NULL) return NULL;
220
221 int tagType = get_camera_metadata_tag_type(tag);
222 if (tagType == -1) {
223 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
224 "Tag (%d) did not have a type", tag);
225 return NULL;
226 }
227 size_t tagSize = Helpers::getTypeSize(tagType);
228
229 camera_metadata_entry entry = metadata->find(tag);
230 if (entry.count == 0) {
231 if (!metadata->exists(tag)) {
232 ALOGV("%s: Tag %d does not have any entries", __FUNCTION__, tag);
233 return NULL;
234 } else {
235 // OK: we will return a 0-sized array.
236 ALOGV("%s: Tag %d had an entry, but it had 0 data", __FUNCTION__,
237 tag);
238 }
239 }
240
241 jsize byteCount = entry.count * tagSize;
242 jbyteArray byteArray = env->NewByteArray(byteCount);
243 if (env->ExceptionCheck()) return NULL;
244
245 // Copy into java array from native array
246 ScopedByteArrayRW arrayWriter(env, byteArray);
247 memcpy(arrayWriter.get(), entry.data.u8, byteCount);
248
249 return byteArray;
250}
251
252static void CameraMetadata_writeValues(JNIEnv *env, jobject thiz, jint tag, jbyteArray src) {
253 ALOGV("%s (tag = %d)", __FUNCTION__, tag);
254
255 CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
256 if (metadata == NULL) return;
257
258 int tagType = get_camera_metadata_tag_type(tag);
259 if (tagType == -1) {
260 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
261 "Tag (%d) did not have a type", tag);
262 return;
263 }
264 size_t tagSize = Helpers::getTypeSize(tagType);
265
266 status_t res;
267
268 if (src == NULL) {
269 // If array is NULL, delete the entry
270 res = metadata->erase(tag);
271 } else {
272 // Copy from java array into native array
273 ScopedByteArrayRO arrayReader(env, src);
274 if (arrayReader.get() == NULL) return;
275
276 res = Helpers::updateAny(metadata, static_cast<uint32_t>(tag),
277 tagType, arrayReader.get(), arrayReader.size());
278 }
279
280 if (res == OK) {
281 return;
282 } else if (res == BAD_VALUE) {
283 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
284 "Src byte array was poorly formed");
285 } else if (res == INVALID_OPERATION) {
286 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
287 "Internal error while trying to update metadata");
288 } else {
289 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
290 "Unknown error (%d) while trying to update "
291 "metadata", res);
292 }
293}
294
Igor Murashkin70725502013-06-25 20:27:06 +0000295static void CameraMetadata_readFromParcel(JNIEnv *env, jobject thiz, jobject parcel) {
296 ALOGV("%s", __FUNCTION__);
297 CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
298 if (metadata == NULL) {
299 return;
300 }
301
302 Parcel* parcelNative = parcelForJavaObject(env, parcel);
303 if (parcelNative == NULL) {
304 jniThrowNullPointerException(env, "parcel");
305 return;
306 }
307
308 status_t err;
309 if ((err = metadata->readFromParcel(parcelNative)) != OK) {
310 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
311 "Failed to read from parcel (error code %d)", err);
312 return;
313 }
314}
315
316static void CameraMetadata_writeToParcel(JNIEnv *env, jobject thiz, jobject parcel) {
317 ALOGV("%s", __FUNCTION__);
318 CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
319 if (metadata == NULL) {
320 return;
321 }
322
323 Parcel* parcelNative = parcelForJavaObject(env, parcel);
324 if (parcelNative == NULL) {
325 jniThrowNullPointerException(env, "parcel");
326 return;
327 }
328
329 status_t err;
330 if ((err = metadata->writeToParcel(parcelNative)) != OK) {
331 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
332 "Failed to write to parcel (error code %d)", err);
333 return;
334 }
335}
336
337} // extern "C"
338
339//-------------------------------------------------
340
341static JNINativeMethod gCameraMetadataMethods[] = {
Igor Murashkinb519cc52013-07-02 11:23:44 -0700342// static methods
Igor Murashkin70725502013-06-25 20:27:06 +0000343 { "nativeClassInit",
344 "()V",
345 (void *)CameraMetadata_classInit },
Igor Murashkinb519cc52013-07-02 11:23:44 -0700346 { "nativeGetTagFromKey",
347 "(Ljava/lang/String;)I",
348 (void *)CameraMetadata_getTagFromKey },
349 { "nativeGetTypeFromTag",
350 "(I)I",
351 (void *)CameraMetadata_getTypeFromTag },
352// instance methods
Igor Murashkin70725502013-06-25 20:27:06 +0000353 { "nativeAllocate",
354 "()J",
355 (void*)CameraMetadata_allocate },
356 { "nativeIsEmpty",
357 "()Z",
358 (void*)CameraMetadata_isEmpty },
359 { "nativeGetEntryCount",
360 "()I",
361 (void*)CameraMetadata_getEntryCount },
362 { "nativeClose",
363 "()V",
364 (void*)CameraMetadata_close },
365 { "nativeSwap",
366 "(L" CAMERA_METADATA_CLASS_NAME ";)V",
367 (void *)CameraMetadata_swap },
Igor Murashkinb519cc52013-07-02 11:23:44 -0700368 { "nativeReadValues",
369 "(I)[B",
370 (void *)CameraMetadata_readValues },
371 { "nativeWriteValues",
372 "(I[B)V",
373 (void *)CameraMetadata_writeValues },
374// Parcelable interface
Igor Murashkin70725502013-06-25 20:27:06 +0000375 { "nativeReadFromParcel",
376 "(Landroid/os/Parcel;)V",
377 (void *)CameraMetadata_readFromParcel },
378 { "nativeWriteToParcel",
379 "(Landroid/os/Parcel;)V",
380 (void *)CameraMetadata_writeToParcel },
381};
382
383struct field {
384 const char *class_name;
385 const char *field_name;
386 const char *field_type;
387 jfieldID *jfield;
388};
389
390static int find_fields(JNIEnv *env, field *fields, int count)
391{
392 for (int i = 0; i < count; i++) {
393 field *f = &fields[i];
394 jclass clazz = env->FindClass(f->class_name);
395 if (clazz == NULL) {
396 ALOGE("Can't find %s", f->class_name);
397 return -1;
398 }
399
400 jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type);
401 if (field == NULL) {
402 ALOGE("Can't find %s.%s", f->class_name, f->field_name);
403 return -1;
404 }
405
406 *(f->jfield) = field;
407 }
408
409 return 0;
410}
411
412// Get all the required offsets in java class and register native functions
413int register_android_hardware_photography_CameraMetadata(JNIEnv *env)
414{
415 // Register native functions
416 return AndroidRuntime::registerNativeMethods(env,
417 CAMERA_METADATA_CLASS_NAME,
418 gCameraMetadataMethods,
419 NELEM(gCameraMetadataMethods));
420}
421
422extern "C" {
423static void CameraMetadata_classInit(JNIEnv *env, jobject thiz) {
424 // XX: Why do this separately instead of doing it in the register function?
425 ALOGV("%s", __FUNCTION__);
426
427 field fields_to_find[] = {
428 { CAMERA_METADATA_CLASS_NAME, "mMetadataPtr", "J", &fields.metadata_ptr },
429 };
430
431 // Do this here instead of in register_native_methods,
432 // since otherwise it will fail to find the fields.
433 if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0)
434 return;
435
436 jclass clazz = env->FindClass(CAMERA_METADATA_CLASS_NAME);
437}
Igor Murashkinb519cc52013-07-02 11:23:44 -0700438
439static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName) {
440
441 ScopedUtfChars keyScoped(env, keyName);
442 const char *key = keyScoped.c_str();
443 if (key == NULL) {
444 // exception thrown by ScopedUtfChars
445 return 0;
446 }
447 size_t keyLength = strlen(key);
448
449 ALOGV("%s (key = '%s')", __FUNCTION__, key);
450
451 // First, find the section by the longest string match
452 const char *section = NULL;
453 size_t sectionIndex = 0;
454 size_t sectionLength = 0;
455 for (size_t i = 0; i < ANDROID_SECTION_COUNT; ++i) {
456 const char *str = camera_metadata_section_names[i];
457 ALOGVV("%s: Trying to match against section '%s'",
458 __FUNCTION__, str);
459 if (strstr(key, str) == key) { // key begins with the section name
460 size_t strLength = strlen(str);
461
462 ALOGVV("%s: Key begins with section name", __FUNCTION__);
463
464 // section name is the longest we've found so far
465 if (section == NULL || sectionLength < strLength) {
466 section = str;
467 sectionIndex = i;
468 sectionLength = strLength;
469
470 ALOGVV("%s: Found new best section (idx %d)", __FUNCTION__, sectionIndex);
471 }
472 }
473 }
474
475 // TODO: vendor ext
476 // TODO: Make above get_camera_metadata_section_from_name ?
477
478 if (section == NULL) {
479 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
480 "Could not find section name for key '%s')", key);
481 return 0;
482 } else {
483 ALOGV("%s: Found matched section '%s' (%d)",
484 __FUNCTION__, section, sectionIndex);
485 }
486
487 // Get the tag name component of the key
488 const char *keyTagName = key + sectionLength + 1; // x.y.z -> z
489 if (sectionLength + 1 >= keyLength) {
490 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
491 "Key length too short for key '%s')", key);
492 }
493
494 // Match rest of name against the tag names in that section only
495 uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd)
496 tagBegin = camera_metadata_section_bounds[sectionIndex][0];
497 tagEnd = camera_metadata_section_bounds[sectionIndex][1];
498
499 uint32_t tag;
500 for (tag = tagBegin; tag < tagEnd; ++tag) {
501 const char *tagName = get_camera_metadata_tag_name(tag);
502
503 if (strcmp(keyTagName, tagName) == 0) {
504 ALOGV("%s: Found matched tag '%s' (%d)",
505 __FUNCTION__, tagName, tag);
506 break;
507 }
508 }
509
510 // TODO: vendor ext
511 // TODO: Make above get_camera_metadata_tag_from_name ?
512
513 if (tag == tagEnd) {
514 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
515 "Could not find tag name for key '%s')", key);
516 return 0;
517 }
518
519 return tag;
520}
521
522static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag) {
523 int tagType = get_camera_metadata_tag_type(tag);
524 if (tagType == -1) {
525 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
526 "Tag (%d) did not have a type", tag);
527 return -1;
528 }
529
530 return tagType;
531}
532
Igor Murashkin70725502013-06-25 20:27:06 +0000533} // extern "C"