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