blob: 5190a375b7accb1937cf203338c3896dd274dec5 [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
Igor Murashkin3710db82013-07-18 20:11:17 -0700270 if (metadata->exists(tag)) {
271 res = metadata->erase(tag);
272 ALOGV("%s: Erase values (res = %d)", __FUNCTION__, res);
273 } else {
274 res = OK;
275 ALOGV("%s: Don't need to erase", __FUNCTION__);
276 }
Igor Murashkinb519cc52013-07-02 11:23:44 -0700277 } else {
278 // Copy from java array into native array
279 ScopedByteArrayRO arrayReader(env, src);
280 if (arrayReader.get() == NULL) return;
281
282 res = Helpers::updateAny(metadata, static_cast<uint32_t>(tag),
283 tagType, arrayReader.get(), arrayReader.size());
Igor Murashkin3710db82013-07-18 20:11:17 -0700284
285 ALOGV("%s: Update values (res = %d)", __FUNCTION__, res);
Igor Murashkinb519cc52013-07-02 11:23:44 -0700286 }
287
288 if (res == OK) {
289 return;
290 } else if (res == BAD_VALUE) {
291 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
292 "Src byte array was poorly formed");
293 } else if (res == INVALID_OPERATION) {
294 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
295 "Internal error while trying to update metadata");
296 } else {
297 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
298 "Unknown error (%d) while trying to update "
299 "metadata", res);
300 }
301}
302
Igor Murashkin70725502013-06-25 20:27:06 +0000303static void CameraMetadata_readFromParcel(JNIEnv *env, jobject thiz, jobject parcel) {
304 ALOGV("%s", __FUNCTION__);
305 CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
306 if (metadata == NULL) {
307 return;
308 }
309
310 Parcel* parcelNative = parcelForJavaObject(env, parcel);
311 if (parcelNative == NULL) {
312 jniThrowNullPointerException(env, "parcel");
313 return;
314 }
315
316 status_t err;
317 if ((err = metadata->readFromParcel(parcelNative)) != OK) {
318 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
319 "Failed to read from parcel (error code %d)", err);
320 return;
321 }
322}
323
324static void CameraMetadata_writeToParcel(JNIEnv *env, jobject thiz, jobject parcel) {
325 ALOGV("%s", __FUNCTION__);
326 CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
327 if (metadata == NULL) {
328 return;
329 }
330
331 Parcel* parcelNative = parcelForJavaObject(env, parcel);
332 if (parcelNative == NULL) {
333 jniThrowNullPointerException(env, "parcel");
334 return;
335 }
336
337 status_t err;
338 if ((err = metadata->writeToParcel(parcelNative)) != OK) {
339 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
340 "Failed to write to parcel (error code %d)", err);
341 return;
342 }
343}
344
345} // extern "C"
346
347//-------------------------------------------------
348
349static JNINativeMethod gCameraMetadataMethods[] = {
Igor Murashkinb519cc52013-07-02 11:23:44 -0700350// static methods
Igor Murashkin70725502013-06-25 20:27:06 +0000351 { "nativeClassInit",
352 "()V",
353 (void *)CameraMetadata_classInit },
Igor Murashkinb519cc52013-07-02 11:23:44 -0700354 { "nativeGetTagFromKey",
355 "(Ljava/lang/String;)I",
356 (void *)CameraMetadata_getTagFromKey },
357 { "nativeGetTypeFromTag",
358 "(I)I",
359 (void *)CameraMetadata_getTypeFromTag },
360// instance methods
Igor Murashkin70725502013-06-25 20:27:06 +0000361 { "nativeAllocate",
362 "()J",
363 (void*)CameraMetadata_allocate },
364 { "nativeIsEmpty",
365 "()Z",
366 (void*)CameraMetadata_isEmpty },
367 { "nativeGetEntryCount",
368 "()I",
369 (void*)CameraMetadata_getEntryCount },
370 { "nativeClose",
371 "()V",
372 (void*)CameraMetadata_close },
373 { "nativeSwap",
374 "(L" CAMERA_METADATA_CLASS_NAME ";)V",
375 (void *)CameraMetadata_swap },
Igor Murashkinb519cc52013-07-02 11:23:44 -0700376 { "nativeReadValues",
377 "(I)[B",
378 (void *)CameraMetadata_readValues },
379 { "nativeWriteValues",
380 "(I[B)V",
381 (void *)CameraMetadata_writeValues },
382// Parcelable interface
Igor Murashkin70725502013-06-25 20:27:06 +0000383 { "nativeReadFromParcel",
384 "(Landroid/os/Parcel;)V",
385 (void *)CameraMetadata_readFromParcel },
386 { "nativeWriteToParcel",
387 "(Landroid/os/Parcel;)V",
388 (void *)CameraMetadata_writeToParcel },
389};
390
391struct field {
392 const char *class_name;
393 const char *field_name;
394 const char *field_type;
395 jfieldID *jfield;
396};
397
398static int find_fields(JNIEnv *env, field *fields, int count)
399{
400 for (int i = 0; i < count; i++) {
401 field *f = &fields[i];
402 jclass clazz = env->FindClass(f->class_name);
403 if (clazz == NULL) {
404 ALOGE("Can't find %s", f->class_name);
405 return -1;
406 }
407
408 jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type);
409 if (field == NULL) {
410 ALOGE("Can't find %s.%s", f->class_name, f->field_name);
411 return -1;
412 }
413
414 *(f->jfield) = field;
415 }
416
417 return 0;
418}
419
420// Get all the required offsets in java class and register native functions
421int register_android_hardware_photography_CameraMetadata(JNIEnv *env)
422{
423 // Register native functions
424 return AndroidRuntime::registerNativeMethods(env,
425 CAMERA_METADATA_CLASS_NAME,
426 gCameraMetadataMethods,
427 NELEM(gCameraMetadataMethods));
428}
429
430extern "C" {
431static void CameraMetadata_classInit(JNIEnv *env, jobject thiz) {
432 // XX: Why do this separately instead of doing it in the register function?
433 ALOGV("%s", __FUNCTION__);
434
435 field fields_to_find[] = {
436 { CAMERA_METADATA_CLASS_NAME, "mMetadataPtr", "J", &fields.metadata_ptr },
437 };
438
439 // Do this here instead of in register_native_methods,
440 // since otherwise it will fail to find the fields.
441 if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0)
442 return;
443
444 jclass clazz = env->FindClass(CAMERA_METADATA_CLASS_NAME);
445}
Igor Murashkinb519cc52013-07-02 11:23:44 -0700446
447static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName) {
448
449 ScopedUtfChars keyScoped(env, keyName);
450 const char *key = keyScoped.c_str();
451 if (key == NULL) {
452 // exception thrown by ScopedUtfChars
453 return 0;
454 }
455 size_t keyLength = strlen(key);
456
457 ALOGV("%s (key = '%s')", __FUNCTION__, key);
458
459 // First, find the section by the longest string match
460 const char *section = NULL;
461 size_t sectionIndex = 0;
462 size_t sectionLength = 0;
463 for (size_t i = 0; i < ANDROID_SECTION_COUNT; ++i) {
464 const char *str = camera_metadata_section_names[i];
465 ALOGVV("%s: Trying to match against section '%s'",
466 __FUNCTION__, str);
467 if (strstr(key, str) == key) { // key begins with the section name
468 size_t strLength = strlen(str);
469
470 ALOGVV("%s: Key begins with section name", __FUNCTION__);
471
472 // section name is the longest we've found so far
473 if (section == NULL || sectionLength < strLength) {
474 section = str;
475 sectionIndex = i;
476 sectionLength = strLength;
477
478 ALOGVV("%s: Found new best section (idx %d)", __FUNCTION__, sectionIndex);
479 }
480 }
481 }
482
483 // TODO: vendor ext
484 // TODO: Make above get_camera_metadata_section_from_name ?
485
486 if (section == NULL) {
487 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
488 "Could not find section name for key '%s')", key);
489 return 0;
490 } else {
491 ALOGV("%s: Found matched section '%s' (%d)",
492 __FUNCTION__, section, sectionIndex);
493 }
494
495 // Get the tag name component of the key
496 const char *keyTagName = key + sectionLength + 1; // x.y.z -> z
497 if (sectionLength + 1 >= keyLength) {
498 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
499 "Key length too short for key '%s')", key);
500 }
501
502 // Match rest of name against the tag names in that section only
503 uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd)
504 tagBegin = camera_metadata_section_bounds[sectionIndex][0];
505 tagEnd = camera_metadata_section_bounds[sectionIndex][1];
506
507 uint32_t tag;
508 for (tag = tagBegin; tag < tagEnd; ++tag) {
509 const char *tagName = get_camera_metadata_tag_name(tag);
510
511 if (strcmp(keyTagName, tagName) == 0) {
512 ALOGV("%s: Found matched tag '%s' (%d)",
513 __FUNCTION__, tagName, tag);
514 break;
515 }
516 }
517
518 // TODO: vendor ext
519 // TODO: Make above get_camera_metadata_tag_from_name ?
520
521 if (tag == tagEnd) {
522 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
523 "Could not find tag name for key '%s')", key);
524 return 0;
525 }
526
527 return tag;
528}
529
530static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag) {
531 int tagType = get_camera_metadata_tag_type(tag);
532 if (tagType == -1) {
533 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
534 "Tag (%d) did not have a type", tag);
535 return -1;
536 }
537
538 return tagType;
539}
540
Igor Murashkin70725502013-06-25 20:27:06 +0000541} // extern "C"