1 /*
2
3 Copyright (c) 2008, The Android Open Source Project
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in
13 the documentation and/or other materials provided with the
14 distribution.
15 * Neither the name of Google, Inc. nor the names of its contributors
16 may be used to endorse or promote products derived from this
17 software without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26 OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 SUCH DAMAGE.
31
32 */
33
34 #include <nativehelper/JNIHelp.h>
35 #include <nativehelper/jni.h>
36
37 #include <assert.h>
38 #include <ctype.h>
39 #include <dlfcn.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <sys/stat.h>
43 #include <utils/Log.h>
44
45 #include "jhead.h"
46
47 #ifndef NELEM
48 #define NELEM(x) ((int)(sizeof(x) / sizeof((x)[0])))
49 #endif
50
51 // Define the line below to turn on poor man's debugging output
52 #undef SUPERDEBUG
53
54 // Various tests
55 #undef REALLOCTEST
56 #undef OUTOFMEMORYTEST1
57
addExifAttibute(JNIEnv * env,jmethodID putMethod,jobject hashMap,char * key,char * value)58 static void addExifAttibute(JNIEnv *env, jmethodID putMethod, jobject hashMap, char* key, char* value) {
59 jstring jkey = (*env)->NewStringUTF(env, key);
60 jstring jvalue = (*env)->NewStringUTF(env, value);
61
62 jobject jobject_of_entryset = (*env)->CallObjectMethod(env, hashMap, putMethod, jkey, jvalue);
63
64 (*env)->ReleaseStringUTFChars(env, jkey, key);
65 (*env)->ReleaseStringUTFChars(env, jvalue, value);
66 }
67
68 extern void ResetJpgfile();
69
loadExifInfo(const char * FileName,int readJPG)70 static int loadExifInfo(const char* FileName, int readJPG) {
71 #ifdef SUPERDEBUG
72 ALOGE("loadExifInfo");
73 #endif
74 int Modified = FALSE;
75 ReadMode_t ReadMode = READ_METADATA;
76 if (readJPG) {
77 // Must add READ_IMAGE else we can't write the JPG back out.
78 ReadMode |= READ_IMAGE;
79 }
80
81 #ifdef SUPERDEBUG
82 ALOGE("ResetJpgfile");
83 #endif
84 ResetJpgfile();
85
86 // Start with an empty image information structure.
87 memset(&ImageInfo, 0, sizeof(ImageInfo));
88 ImageInfo.FlashUsed = -1;
89 ImageInfo.MeteringMode = -1;
90 ImageInfo.Whitebalance = -1;
91
92 // Store file date/time.
93 {
94 struct stat st;
95 if (stat(FileName, &st) >= 0) {
96 ImageInfo.FileDateTime = st.st_mtime;
97 ImageInfo.FileSize = st.st_size;
98 }
99 }
100
101 strncpy(ImageInfo.FileName, FileName, PATH_MAX);
102 #ifdef SUPERDEBUG
103 ALOGE("ReadJpegFile");
104 #endif
105 return ReadJpegFile(FileName, ReadMode);
106 }
107
saveJPGFile(const char * filename)108 static void saveJPGFile(const char* filename) {
109 char backupName[400];
110 struct stat buf;
111
112 #ifdef SUPERDEBUG
113 ALOGE("Modified: %s\n", filename);
114 #endif
115
116 strncpy(backupName, filename, 395);
117 strcat(backupName, ".t");
118
119 // Remove any .old file name that may pre-exist
120 #ifdef SUPERDEBUG
121 ALOGE("removing backup %s", backupName);
122 #endif
123 unlink(backupName);
124
125 // Rename the old file.
126 #ifdef SUPERDEBUG
127 ALOGE("rename %s to %s", filename, backupName);
128 #endif
129 rename(filename, backupName);
130
131 // Write the new file.
132 #ifdef SUPERDEBUG
133 ALOGE("WriteJpegFile %s", filename);
134 #endif
135 if (WriteJpegFile(filename)) {
136
137 // Copy the access rights from original file
138 #ifdef SUPERDEBUG
139 ALOGE("stating old file %s", backupName);
140 #endif
141 if (stat(backupName, &buf) == 0){
142 // set Unix access rights and time to new file
143 struct utimbuf mtime;
144 chmod(filename, buf.st_mode);
145
146 mtime.actime = buf.st_mtime;
147 mtime.modtime = buf.st_mtime;
148
149 utime(filename, &mtime);
150 }
151
152 // Now that we are done, remove original file.
153 #ifdef SUPERDEBUG
154 ALOGE("unlinking old file %s", backupName);
155 #endif
156 unlink(backupName);
157 #ifdef SUPERDEBUG
158 ALOGE("returning from saveJPGFile");
159 #endif
160 } else {
161 #ifdef SUPERDEBUG
162 ALOGE("WriteJpegFile failed, restoring from backup file");
163 #endif
164 // move back the backup file
165 rename(backupName, filename);
166 }
167 }
168
copyThumbnailData(uchar * thumbnailData,int thumbnailLen)169 void copyThumbnailData(uchar* thumbnailData, int thumbnailLen) {
170 #ifdef SUPERDEBUG
171 ALOGE("******************************** copyThumbnailData\n");
172 #endif
173 Section_t* ExifSection = FindSection(M_EXIF);
174 if (ExifSection == NULL) {
175 return;
176 }
177 int NewExifSize = ImageInfo.ThumbnailOffset+8+thumbnailLen;
178 ExifSection->Data = (uchar *)realloc(ExifSection->Data, NewExifSize);
179 if (ExifSection->Data == NULL) {
180 return;
181 }
182 uchar* ThumbnailPointer = ExifSection->Data+ImageInfo.ThumbnailOffset+8;
183
184 memcpy(ThumbnailPointer, thumbnailData, thumbnailLen);
185
186 ImageInfo.ThumbnailSize = thumbnailLen;
187
188 Put32u(ExifSection->Data+ImageInfo.ThumbnailSizeOffset+8, thumbnailLen);
189
190 ExifSection->Data[0] = (uchar)(NewExifSize >> 8);
191 ExifSection->Data[1] = (uchar)NewExifSize;
192 ExifSection->Size = NewExifSize;
193 }
194
saveAttributes(JNIEnv * env,jobject jobj,jstring jfilename,jstring jattributes)195 static void saveAttributes(JNIEnv *env, jobject jobj, jstring jfilename, jstring jattributes)
196 {
197 #ifdef SUPERDEBUG
198 ALOGE("******************************** saveAttributes\n");
199 #endif
200 // format of attributes string passed from java:
201 // "attrCnt attr1=valueLen value1attr2=value2Len value2..."
202 // example input: "4 ImageLength=4 1024Model=6 FooImageWidth=4 1280Make=3 FOO"
203 ExifElement_t* exifElementTable = NULL;
204 const char* filename = NULL;
205 uchar* thumbnailData = NULL;
206 int attrCnt = 0;
207 const char* attributes = (*env)->GetStringUTFChars(env, jattributes, NULL);
208 if (attributes == NULL) {
209 goto exit;
210 }
211 #ifdef SUPERDEBUG
212 ALOGE("attributes %s\n", attributes);
213 #endif
214
215 // Get the number of attributes - it's the first number in the string.
216 attrCnt = atoi(attributes);
217 char* attrPtr = strchr(attributes, ' ') + 1;
218 #ifdef SUPERDEBUG
219 ALOGE("attribute count %d attrPtr %s\n", attrCnt, attrPtr);
220 #endif
221
222 // Load all the hash exif elements into a more c-like structure
223 exifElementTable = malloc(sizeof(ExifElement_t) * attrCnt);
224 if (exifElementTable == NULL) {
225 goto exit;
226 }
227 #ifdef OUTOFMEMORYTEST1
228 goto exit;
229 #endif
230
231 int i;
232 char tag[100];
233 int hasDateTimeTag = FALSE;
234 int gpsTagCount = 0;
235 int exifTagCount = 0;
236
237 for (i = 0; i < attrCnt; i++) {
238 // get an element from the attribute string and add it to the c structure
239 // first, extract the attribute name
240 char* tagEnd = strchr(attrPtr, '=');
241 if (tagEnd == 0) {
242 #ifdef SUPERDEBUG
243 ALOGE("saveAttributes: couldn't find end of tag");
244 #endif
245 goto exit;
246 }
247 if (tagEnd - attrPtr > 99) {
248 #ifdef SUPERDEBUG
249 ALOGE("saveAttributes: attribute tag way too long");
250 #endif
251 goto exit;
252 }
253 memcpy(tag, attrPtr, tagEnd - attrPtr);
254 tag[tagEnd - attrPtr] = 0;
255
256 if (IsGpsTag(tag)) {
257 exifElementTable[i].GpsTag = TRUE;
258 exifElementTable[i].Tag = GpsTagNameToValue(tag);
259 ++gpsTagCount;
260 } else {
261 exifElementTable[i].GpsTag = FALSE;
262 exifElementTable[i].Tag = TagNameToValue(tag);
263 ++exifTagCount;
264 }
265 attrPtr = tagEnd + 1;
266
267 if (IsDateTimeTag(exifElementTable[i].Tag)) {
268 hasDateTimeTag = TRUE;
269 }
270
271 // next get the length of the attribute value
272 int valueLen = atoi(attrPtr);
273 attrPtr = strchr(attrPtr, ' ') + 1;
274 if (attrPtr == 0) {
275 #ifdef SUPERDEBUG
276 ALOGE("saveAttributes: couldn't find end of value len");
277 #endif
278 goto exit;
279 }
280 exifElementTable[i].Value = malloc(valueLen + 1);
281 if (exifElementTable[i].Value == NULL) {
282 goto exit;
283 }
284 memcpy(exifElementTable[i].Value, attrPtr, valueLen);
285 exifElementTable[i].Value[valueLen] = 0;
286 exifElementTable[i].DataLength = valueLen;
287
288 attrPtr += valueLen;
289
290 #ifdef SUPERDEBUG
291 ALOGE("tag %s id %d value %s data length=%d isGps=%d", tag, exifElementTable[i].Tag,
292 exifElementTable[i].Value, exifElementTable[i].DataLength, exifElementTable[i].GpsTag);
293 #endif
294 }
295
296 filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
297 #ifdef SUPERDEBUG
298 ALOGE("Call loadAttributes() with filename is %s. Loading exif info\n", filename);
299 #endif
300 loadExifInfo(filename, TRUE);
301
302 #ifdef SUPERDEBUG
303 // DumpExifMap = TRUE;
304 ShowTags = TRUE;
305 ShowImageInfo(TRUE);
306 ALOGE("create exif 2");
307 #endif
308
309 // If the jpg file has a thumbnail, preserve it.
310 int thumbnailLength = ImageInfo.ThumbnailSize;
311 if (ImageInfo.ThumbnailOffset) {
312 Section_t* ExifSection = FindSection(M_EXIF);
313 if (ExifSection) {
314 uchar* thumbnailPointer = ExifSection->Data + ImageInfo.ThumbnailOffset + 8;
315 thumbnailData = (uchar*)malloc(ImageInfo.ThumbnailSize);
316 // if the malloc fails, we just won't copy the thumbnail
317 if (thumbnailData) {
318 memcpy(thumbnailData, thumbnailPointer, thumbnailLength);
319 }
320 }
321 }
322
323 create_EXIF(exifElementTable, exifTagCount, gpsTagCount, hasDateTimeTag);
324
325 if (thumbnailData) {
326 copyThumbnailData(thumbnailData, thumbnailLength);
327 }
328
329 exit:
330 #ifdef SUPERDEBUG
331 ALOGE("cleaning up now in saveAttributes");
332 #endif
333 // try to clean up resources
334 if (attributes) {
335 (*env)->ReleaseStringUTFChars(env, jattributes, attributes);
336 }
337 if (filename) {
338 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
339 }
340 if (exifElementTable) {
341 // free the table
342 for (i = 0; i < attrCnt; i++) {
343 free(exifElementTable[i].Value);
344 }
345 free(exifElementTable);
346 }
347 if (thumbnailData) {
348 free(thumbnailData);
349 }
350 #ifdef SUPERDEBUG
351 ALOGE("returning from saveAttributes");
352 #endif
353
354 // Temporarily saving these commented out lines because they represent a lot of figuring out
355 // patterns for JNI.
356 // // Get link to Method "entrySet"
357 // jmethodID entrySetMethod = (*env)->GetMethodID(env, jclass_of_hashmap, "entrySet", "()Ljava/util/Set;");
358 //
359 // // Invoke the "entrySet" method on the HashMap object
360 // jobject jobject_of_entryset = (*env)->CallObjectMethod(env, hashMap, entrySetMethod);
361 //
362 // // Get the Set Class
363 // jclass jclass_of_set = (*env)->FindClass(env, "java/util/Set");
364 //
365 // if (jclass_of_set == 0) {
366 // printf("java/util/Set lookup failed\n");
367 // return;
368 // }
369 //
370 // // Get link to Method "iterator"
371 // jmethodID iteratorMethod = (*env)->GetMethodID(env, jclass_of_set, "iterator", "()Ljava/util/Iterator;");
372 //
373 // // Invoke the "iterator" method on the jobject_of_entryset variable of type Set
374 // jobject jobject_of_iterator = (*env)->CallObjectMethod(env, jobject_of_entryset, iteratorMethod);
375 //
376 // // Get the "Iterator" class
377 // jclass jclass_of_iterator = (*env)->FindClass(env, "java/util/Iterator");
378 //
379 // // Get link to Method "hasNext"
380 // jmethodID hasNextMethod = (*env)->GetMethodID(env, jclass_of_iterator, "hasNext", "()Z");
381 //
382 // // Invoke - Get the value hasNextMethod
383 // jboolean bHasNext = (*env)->CallBooleanMethod(env, jobject_of_iterator, hasNextMethod);
384
385 // // Get link to Method "hasNext"
386 // jmethodID nextMethod = (*env)->GetMethodID(env, jclass_of_iterator, "next", "()Ljava/util/Map/Entry;");
387 //
388 // jclass jclass_of_mapentry = (*env)->FindClass(env, "java/util/Map/Entry");
389 //
390 // jmethodID getKeyMethod = (*env)->GetMethodID(env, jclass_of_mapentry, "getKey", "()Ljava/lang/Object");
391 //
392 // jmethodID getValueMethod = (*env)->GetMethodID(env, jclass_of_mapentry, "getValue", "()Ljava/lang/Object");
393 }
394
appendThumbnail(JNIEnv * env,jobject jobj,jstring jfilename,jstring jthumbnailfilename)395 static jboolean appendThumbnail(JNIEnv *env, jobject jobj, jstring jfilename, jstring jthumbnailfilename)
396 {
397 #ifdef SUPERDEBUG
398 ALOGE("******************************** appendThumbnail\n");
399 #endif
400
401 const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
402 if (filename == NULL) {
403 return JNI_FALSE;
404 }
405 const char* thumbnailfilename = (*env)->GetStringUTFChars(env, jthumbnailfilename, NULL);
406 if (thumbnailfilename == NULL) {
407 return JNI_FALSE;
408 }
409 #ifdef SUPERDEBUG
410 ALOGE("*******before actual call to ReplaceThumbnail\n");
411 ShowImageInfo(TRUE);
412 #endif
413 ReplaceThumbnail(thumbnailfilename);
414 #ifdef SUPERDEBUG
415 ShowImageInfo(TRUE);
416 #endif
417 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
418 (*env)->ReleaseStringUTFChars(env, jthumbnailfilename, thumbnailfilename);
419
420 DiscardData();
421 return JNI_TRUE;
422 }
423
commitChanges(JNIEnv * env,jobject jobj,jstring jfilename)424 static void commitChanges(JNIEnv *env, jobject jobj, jstring jfilename)
425 {
426 #ifdef SUPERDEBUG
427 ALOGE("******************************** commitChanges\n");
428 #endif
429 const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
430 if (filename) {
431 saveJPGFile(filename);
432 DiscardData();
433 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
434 }
435 }
436
getThumbnail(JNIEnv * env,jobject jobj,jstring jfilename)437 static jbyteArray getThumbnail(JNIEnv *env, jobject jobj, jstring jfilename)
438 {
439 #ifdef SUPERDEBUG
440 ALOGE("******************************** getThumbnail\n");
441 #endif
442
443 const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
444 if (filename) {
445 loadExifInfo(filename, FALSE);
446 Section_t* ExifSection = FindSection(M_EXIF);
447 if (ExifSection == NULL || ImageInfo.ThumbnailSize == 0) {
448 #ifdef SUPERDEBUG
449 ALOGE("no exif section or size == 0, so no thumbnail\n");
450 #endif
451 goto noThumbnail;
452 }
453 const jbyte* thumbnailPointer =
454 (const jbyte*) (ExifSection->Data + ImageInfo.ThumbnailOffset + 8);
455
456 jbyteArray byteArray = (*env)->NewByteArray(env, ImageInfo.ThumbnailSize);
457 if (byteArray == NULL) {
458 #ifdef SUPERDEBUG
459 ALOGE("couldn't allocate thumbnail memory, so no thumbnail\n");
460 #endif
461 goto noThumbnail;
462 }
463 (*env)->SetByteArrayRegion(env, byteArray, 0, ImageInfo.ThumbnailSize, thumbnailPointer);
464 #ifdef SUPERDEBUG
465 ALOGE("thumbnail size %d\n", ImageInfo.ThumbnailSize);
466 #endif
467 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
468 DiscardData();
469 return byteArray;
470 }
471 noThumbnail:
472 if (filename) {
473 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
474 }
475 DiscardData();
476 return NULL;
477 }
478
getThumbnailRange(JNIEnv * env,jobject jobj,jstring jfilename)479 static jlongArray getThumbnailRange(JNIEnv *env, jobject jobj, jstring jfilename) {
480 jlongArray resultArray = NULL;
481 const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
482 if (filename) {
483 loadExifInfo(filename, FALSE);
484 Section_t* ExifSection = FindSection(M_EXIF);
485 if (ExifSection == NULL || ImageInfo.ThumbnailSize == 0) {
486 goto done;
487 }
488
489 jlong result[2];
490 result[0] = ExifSection->Offset + ImageInfo.ThumbnailOffset + 8;
491 result[1] = ImageInfo.ThumbnailSize;
492
493 resultArray = (*env)->NewLongArray(env, 2);
494 if (resultArray == NULL) {
495 goto done;
496 }
497
498 (*env)->SetLongArrayRegion(env, resultArray, 0, 2, result);
499 }
500 done:
501 if (filename) {
502 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
503 }
504 DiscardData();
505 return resultArray;
506 }
507
508 static int attributeCount; // keep track of how many attributes we've added
509
510 // returns new buffer length
addKeyValueString(char ** buf,int bufLen,const char * key,const char * value)511 static int addKeyValueString(char** buf, int bufLen, const char* key, const char* value) {
512 // Appends to buf like this: "ImageLength=4 1024"
513
514 char valueLen[15];
515 snprintf(valueLen, 15, "=%d ", (int)strlen(value));
516
517 // check to see if buf has enough room to append
518 int len = strlen(key) + strlen(valueLen) + strlen(value);
519 int newLen = strlen(*buf) + len;
520 if (newLen >= bufLen) {
521 #ifdef REALLOCTEST
522 bufLen = newLen + 5;
523 ALOGE("reallocing to %d", bufLen);
524 #else
525 bufLen = newLen + 500;
526 #endif
527 *buf = realloc(*buf, bufLen);
528 if (*buf == NULL) {
529 return 0;
530 }
531 }
532 // append the new attribute and value
533 snprintf(*buf + strlen(*buf), bufLen, "%s%s%s", key, valueLen, value);
534 #ifdef SUPERDEBUG
535 ALOGE("buf %s", *buf);
536 #endif
537 ++attributeCount;
538 return bufLen;
539 }
540
541 // returns new buffer length
addKeyValueInt(char ** buf,int bufLen,const char * key,int value)542 static int addKeyValueInt(char** buf, int bufLen, const char* key, int value) {
543 char valueStr[20];
544 snprintf(valueStr, 20, "%d", value);
545
546 return addKeyValueString(buf, bufLen, key, valueStr);
547 }
548
549 // returns new buffer length
addKeyValueDouble(char ** buf,int bufLen,const char * key,double value,const char * format)550 static int addKeyValueDouble(char** buf, int bufLen, const char* key, double value, const char* format) {
551 char valueStr[30];
552 snprintf(valueStr, 30, format, value);
553
554 return addKeyValueString(buf, bufLen, key, valueStr);
555 }
556
557 // Returns new buffer length. Rational value will be appended as "numerator/denominator".
addKeyValueRational(char ** buf,int bufLen,const char * key,rat_t value)558 static int addKeyValueRational(char** buf, int bufLen, const char* key, rat_t value) {
559 char valueStr[25];
560 snprintf(valueStr, sizeof(valueStr), "%u/%u", value.num, value.denom);
561 return addKeyValueString(buf, bufLen, key, valueStr);
562 }
563
getAttributes(JNIEnv * env,jobject jobj,jstring jfilename)564 static jstring getAttributes(JNIEnv *env, jobject jobj, jstring jfilename)
565 {
566 #ifdef SUPERDEBUG
567 ALOGE("******************************** getAttributes\n");
568 #endif
569 const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
570 loadExifInfo(filename, FALSE);
571 #ifdef SUPERDEBUG
572 ShowImageInfo(TRUE);
573 #endif
574 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
575
576 attributeCount = 0;
577 #ifdef REALLOCTEST
578 int bufLen = 5;
579 #else
580 int bufLen = 1000;
581 #endif
582 char* buf = malloc(bufLen);
583 if (buf == NULL) {
584 return NULL;
585 }
586 *buf = 0; // start the string out at zero length
587
588 // save a fake "hasThumbnail" tag to pass to the java ExifInterface
589 bufLen = addKeyValueString(&buf, bufLen, "hasThumbnail",
590 ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailAtEnd == FALSE || ImageInfo.ThumbnailSize == 0 ?
591 "false" : "true");
592 if (bufLen == 0) return NULL;
593
594 if (ImageInfo.CameraMake[0]) {
595 bufLen = addKeyValueString(&buf, bufLen, "Make", ImageInfo.CameraMake);
596 if (bufLen == 0) return NULL;
597 }
598 if (ImageInfo.CameraModel[0]) {
599 bufLen = addKeyValueString(&buf, bufLen, "Model", ImageInfo.CameraModel);
600 if (bufLen == 0) return NULL;
601 }
602 if (ImageInfo.DateTime[0]) {
603 bufLen = addKeyValueString(&buf, bufLen, "DateTime", ImageInfo.DateTime);
604 if (bufLen == 0) return NULL;
605 }
606 if (ImageInfo.DigitizedTime[0]) {
607 bufLen = addKeyValueString(&buf, bufLen, "DateTimeDigitized", ImageInfo.DigitizedTime);
608 if (bufLen == 0) return NULL;
609 }
610 if (ImageInfo.SubSecTime[0]) {
611 bufLen = addKeyValueString(&buf, bufLen, "SubSecTime", ImageInfo.SubSecTime);
612 if (bufLen == 0) return NULL;
613 }
614 if (ImageInfo.SubSecTimeOrig[0]) {
615 bufLen = addKeyValueString(&buf, bufLen, "SubSecTimeOriginal", ImageInfo.SubSecTimeOrig);
616 if (bufLen == 0) return NULL;
617 }
618 if (ImageInfo.SubSecTimeDig[0]) {
619 bufLen = addKeyValueString(&buf, bufLen, "SubSecTimeDigitized", ImageInfo.SubSecTimeDig);
620 if (bufLen == 0) return NULL;
621 }
622
623 bufLen = addKeyValueInt(&buf, bufLen, "ImageWidth", ImageInfo.Width);
624 if (bufLen == 0) return NULL;
625
626 bufLen = addKeyValueInt(&buf, bufLen, "ImageLength", ImageInfo.Height);
627 if (bufLen == 0) return NULL;
628
629 bufLen = addKeyValueInt(&buf, bufLen, "Orientation", ImageInfo.Orientation);
630 if (bufLen == 0) return NULL;
631
632 if (ImageInfo.FlashUsed >= 0) {
633 bufLen = addKeyValueInt(&buf, bufLen, "Flash", ImageInfo.FlashUsed);
634 if (bufLen == 0) return NULL;
635 }
636
637 if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0) {
638 bufLen = addKeyValueRational(&buf, bufLen, "FocalLength", ImageInfo.FocalLength);
639 if (bufLen == 0) return NULL;
640 }
641
642 if (ImageInfo.DigitalZoomRatio > 1.0){
643 // Digital zoom used. Shame on you!
644 bufLen = addKeyValueDouble(&buf, bufLen, "DigitalZoomRatio", ImageInfo.DigitalZoomRatio, "%5.3f");
645 if (bufLen == 0) return NULL;
646 }
647
648 if (ImageInfo.ExposureTime){
649 const char* format;
650 if (ImageInfo.ExposureTime < 0.010){
651 format = "%6.4f";
652 } else {
653 format = "%5.3f";
654 }
655
656 bufLen = addKeyValueDouble(&buf, bufLen, "ExposureTime", (double)ImageInfo.ExposureTime, format);
657 if (bufLen == 0) return NULL;
658 }
659
660 if (ImageInfo.ApertureFNumber){
661 bufLen = addKeyValueDouble(&buf, bufLen, "FNumber", (double)ImageInfo.ApertureFNumber, "%5.3f");
662 if (bufLen == 0) return NULL;
663 }
664
665 if (ImageInfo.Distance){
666 bufLen = addKeyValueDouble(&buf, bufLen, "SubjectDistance", (double)ImageInfo.Distance, "%4.2f");
667 if (bufLen == 0) return NULL;
668 }
669
670 if (ImageInfo.ISOequivalent){
671 bufLen = addKeyValueInt(&buf, bufLen, "ISOSpeedRatings", ImageInfo.ISOequivalent);
672 if (bufLen == 0) return NULL;
673 }
674
675 if (ImageInfo.ExposureBias){
676 // If exposure bias was specified, but set to zero, presumably its no bias at all,
677 // so only show it if its nonzero.
678 bufLen = addKeyValueDouble(&buf, bufLen, "ExposureBiasValue", (double)ImageInfo.ExposureBias, "%4.2f");
679 if (bufLen == 0) return NULL;
680 }
681
682 if (ImageInfo.Whitebalance >= 0) {
683 bufLen = addKeyValueInt(&buf, bufLen, "WhiteBalance", ImageInfo.Whitebalance);
684 if (bufLen == 0) return NULL;
685 }
686
687 bufLen = addKeyValueInt(&buf, bufLen, "LightSource", ImageInfo.LightSource);
688 if (bufLen == 0) return NULL;
689
690
691 if (ImageInfo.MeteringMode) {
692 bufLen = addKeyValueInt(&buf, bufLen, "MeteringMode", ImageInfo.MeteringMode);
693 if (bufLen == 0) return NULL;
694 }
695
696 if (ImageInfo.ExposureProgram) {
697 bufLen = addKeyValueInt(&buf, bufLen, "ExposureProgram", ImageInfo.ExposureProgram);
698 if (bufLen == 0) return NULL;
699 }
700
701 if (ImageInfo.ExposureMode) {
702 bufLen = addKeyValueInt(&buf, bufLen, "ExposureMode", ImageInfo.ExposureMode);
703 if (bufLen == 0) return NULL;
704 }
705
706 if (ImageInfo.GpsInfoPresent) {
707 if (ImageInfo.GpsLatRaw[0]) {
708 bufLen = addKeyValueString(&buf, bufLen, "GPSLatitude", ImageInfo.GpsLatRaw);
709 if (bufLen == 0) return NULL;
710 }
711 if (ImageInfo.GpsLatRef[0]) {
712 bufLen = addKeyValueString(&buf, bufLen, "GPSLatitudeRef", ImageInfo.GpsLatRef);
713 if (bufLen == 0) return NULL;
714 }
715 if (ImageInfo.GpsLongRaw[0]) {
716 bufLen = addKeyValueString(&buf, bufLen, "GPSLongitude", ImageInfo.GpsLongRaw);
717 if (bufLen == 0) return NULL;
718 }
719 if (ImageInfo.GpsLongRef[0]) {
720 bufLen = addKeyValueString(&buf, bufLen, "GPSLongitudeRef", ImageInfo.GpsLongRef);
721 if (bufLen == 0) return NULL;
722 }
723 if (ImageInfo.GpsAlt[0]) {
724 bufLen = addKeyValueRational(&buf, bufLen, "GPSAltitude", ImageInfo.GpsAltRaw);
725 bufLen = addKeyValueInt(&buf, bufLen, "GPSAltitudeRef", ImageInfo.GpsAltRef);
726 if (bufLen == 0) return NULL;
727 }
728 if (ImageInfo.GpsDateStamp[0]) {
729 bufLen = addKeyValueString(&buf, bufLen, "GPSDateStamp", ImageInfo.GpsDateStamp);
730 if (bufLen == 0) return NULL;
731 }
732 if (ImageInfo.GpsTimeStamp[0]) {
733 bufLen = addKeyValueString(&buf, bufLen, "GPSTimeStamp", ImageInfo.GpsTimeStamp);
734 if (bufLen == 0) return NULL;
735 }
736 if (ImageInfo.GpsProcessingMethod[0]) {
737 bufLen = addKeyValueString(&buf, bufLen, "GPSProcessingMethod", ImageInfo.GpsProcessingMethod);
738 if (bufLen == 0) return NULL;
739 }
740 }
741
742 if (ImageInfo.Comments[0]) {
743 bufLen = addKeyValueString(&buf, bufLen, "UserComment", ImageInfo.Comments);
744 if (bufLen == 0) return NULL;
745 }
746
747 // put the attribute count at the beginnnig of the string
748 int finalBufLen = strlen(buf) + 20;
749 char* finalResult = malloc(finalBufLen);
750 if (finalResult == NULL) {
751 free(buf);
752 return NULL;
753 }
754 snprintf(finalResult, finalBufLen, "%d %s", attributeCount, buf);
755 int k;
756 for (k = 0; k < finalBufLen; k++)
757 if (!isascii(finalResult[k]))
758 finalResult[k] = '?';
759 free(buf);
760
761 #ifdef SUPERDEBUG
762 ALOGE("*********Returning result \"%s\"", finalResult);
763 #endif
764 jstring result = ((*env)->NewStringUTF(env, finalResult));
765 free(finalResult);
766 DiscardData();
767 return result;
768 }
769
770 static const char *classPathName = "android/media/ExifInterface";
771
772 static JNINativeMethod methods[] = {
773 {"saveAttributesNative", "(Ljava/lang/String;Ljava/lang/String;)V", (void*)saveAttributes },
774 {"getAttributesNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getAttributes },
775 {"appendThumbnailNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)appendThumbnail },
776 {"commitChangesNative", "(Ljava/lang/String;)V", (void*)commitChanges },
777 {"getThumbnailNative", "(Ljava/lang/String;)[B", (void*)getThumbnail },
778 {"getThumbnailRangeNative", "(Ljava/lang/String;)[J", (void*)getThumbnailRange },
779 };
780
781 /*
782 * Register several native methods for one class.
783 */
registerNativeMethods(JNIEnv * env,const char * className,JNINativeMethod * gMethods,int numMethods)784 static int registerNativeMethods(JNIEnv* env, const char* className,
785 JNINativeMethod* gMethods, int numMethods)
786 {
787 jclass clazz;
788
789 clazz = (*env)->FindClass(env, className);
790 if (clazz == NULL) {
791 fprintf(stderr,
792 "Native registration unable to find class '%s'\n", className);
793 return JNI_FALSE;
794 }
795 if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
796 fprintf(stderr, "RegisterNatives failed for '%s'\n", className);
797 return JNI_FALSE;
798 }
799
800 return JNI_TRUE;
801 }
802
803 /*
804 * Register native methods for all classes we know about.
805 */
registerNatives(JNIEnv * env)806 static int registerNatives(JNIEnv* env)
807 {
808 return jniRegisterNativeMethods(env, classPathName,
809 methods, NELEM(methods));
810 }
811
812 /*
813 * Set some test stuff up.
814 *
815 * Returns the JNI version on success, -1 on failure.
816 */
JNI_OnLoad(JavaVM * vm,void * reserved)817 __attribute__ ((visibility("default"))) jint JNI_OnLoad(JavaVM* vm, void* reserved)
818 {
819 JNIEnv* env = NULL;
820 jint result = -1;
821
822 if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
823 fprintf(stderr, "ERROR: GetEnv failed\n");
824 goto bail;
825 }
826 assert(env != NULL);
827
828 printf("In mgmain JNI_OnLoad\n");
829
830 if (registerNatives(env) < 0) {
831 fprintf(stderr, "ERROR: Exif native registration failed\n");
832 goto bail;
833 }
834
835 /* success -- return valid version number */
836 result = JNI_VERSION_1_4;
837
838 bail:
839 return result;
840 }
841