• 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 <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     LOGE("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     LOGE("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     LOGE("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     LOGE("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     LOGE("removing backup %s", backupName);
122 #endif
123     unlink(backupName);
124 
125     // Rename the old file.
126 #ifdef SUPERDEBUG
127     LOGE("rename %s to %s", filename, backupName);
128 #endif
129     rename(filename, backupName);
130 
131     // Write the new file.
132 #ifdef SUPERDEBUG
133     LOGE("WriteJpegFile %s", filename);
134 #endif
135     if (WriteJpegFile(filename)) {
136 
137         // Copy the access rights from original file
138 #ifdef SUPERDEBUG
139         LOGE("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         LOGE("unlinking old file %s", backupName);
155 #endif
156         unlink(backupName);
157 #ifdef SUPERDEBUG
158         LOGE("returning from saveJPGFile");
159 #endif
160     } else {
161 #ifdef SUPERDEBUG
162         LOGE("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     LOGE("******************************** 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     LOGE("******************************** 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     LOGE("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     LOGE("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 gpsTagCount = 0;
234     int exifTagCount = 0;
235 
236     for (i = 0; i < attrCnt; i++) {
237         // get an element from the attribute string and add it to the c structure
238         // first, extract the attribute name
239         char* tagEnd = strchr(attrPtr, '=');
240         if (tagEnd == 0) {
241 #ifdef SUPERDEBUG
242             LOGE("saveAttributes: couldn't find end of tag");
243 #endif
244             goto exit;
245         }
246         if (tagEnd - attrPtr > 99) {
247 #ifdef SUPERDEBUG
248             LOGE("saveAttributes: attribute tag way too long");
249 #endif
250             goto exit;
251         }
252         memcpy(tag, attrPtr, tagEnd - attrPtr);
253         tag[tagEnd - attrPtr] = 0;
254 
255         if (IsGpsTag(tag)) {
256             exifElementTable[i].GpsTag = TRUE;
257             exifElementTable[i].Tag = GpsTagNameToValue(tag);
258             ++gpsTagCount;
259         } else {
260             exifElementTable[i].GpsTag = FALSE;
261             exifElementTable[i].Tag = TagNameToValue(tag);
262             ++exifTagCount;
263         }
264         attrPtr = tagEnd + 1;
265 
266         // next get the length of the attribute value
267         int valueLen = atoi(attrPtr);
268         attrPtr = strchr(attrPtr, ' ') + 1;
269         if (attrPtr == 0) {
270 #ifdef SUPERDEBUG
271             LOGE("saveAttributes: couldn't find end of value len");
272 #endif
273             goto exit;
274         }
275         exifElementTable[i].Value = malloc(valueLen + 1);
276         if (exifElementTable[i].Value == NULL) {
277             goto exit;
278         }
279         memcpy(exifElementTable[i].Value, attrPtr, valueLen);
280         exifElementTable[i].Value[valueLen] = 0;
281         exifElementTable[i].DataLength = valueLen;
282 
283         attrPtr += valueLen;
284 
285 #ifdef SUPERDEBUG
286         LOGE("tag %s id %d value %s data length=%d isGps=%d", tag, exifElementTable[i].Tag,
287             exifElementTable[i].Value, exifElementTable[i].DataLength, exifElementTable[i].GpsTag);
288 #endif
289     }
290 
291     filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
292 #ifdef SUPERDEBUG
293     LOGE("Call loadAttributes() with filename is %s. Loading exif info\n", filename);
294 #endif
295     loadExifInfo(filename, TRUE);
296 
297 #ifdef SUPERDEBUG
298 //    DumpExifMap = TRUE;
299     ShowTags = TRUE;
300     ShowImageInfo(TRUE);
301     LOGE("create exif 2");
302 #endif
303 
304     // If the jpg file has a thumbnail, preserve it.
305     int thumbnailLength = ImageInfo.ThumbnailSize;
306     if (ImageInfo.ThumbnailOffset) {
307         Section_t* ExifSection = FindSection(M_EXIF);
308         if (ExifSection) {
309             uchar* thumbnailPointer = ExifSection->Data + ImageInfo.ThumbnailOffset + 8;
310             thumbnailData = (uchar*)malloc(ImageInfo.ThumbnailSize);
311             // if the malloc fails, we just won't copy the thumbnail
312             if (thumbnailData) {
313                 memcpy(thumbnailData, thumbnailPointer, thumbnailLength);
314             }
315         }
316     }
317 
318     create_EXIF(exifElementTable, exifTagCount, gpsTagCount);
319 
320     if (thumbnailData) {
321         copyThumbnailData(thumbnailData, thumbnailLength);
322     }
323 
324 exit:
325 #ifdef SUPERDEBUG
326     LOGE("cleaning up now in saveAttributes");
327 #endif
328     // try to clean up resources
329     if (attributes) {
330         (*env)->ReleaseStringUTFChars(env, jattributes, attributes);
331     }
332     if (filename) {
333         (*env)->ReleaseStringUTFChars(env, jfilename, filename);
334     }
335     if (exifElementTable) {
336         // free the table
337         for (i = 0; i < attrCnt; i++) {
338             free(exifElementTable[i].Value);
339         }
340         free(exifElementTable);
341     }
342     if (thumbnailData) {
343         free(thumbnailData);
344     }
345 #ifdef SUPERDEBUG
346     LOGE("returning from saveAttributes");
347 #endif
348 
349 // Temporarily saving these commented out lines because they represent a lot of figuring out
350 // patterns for JNI.
351 //    // Get link to Method "entrySet"
352 //    jmethodID entrySetMethod = (*env)->GetMethodID(env, jclass_of_hashmap, "entrySet", "()Ljava/util/Set;");
353 //
354 //    // Invoke the "entrySet" method on the HashMap object
355 //    jobject jobject_of_entryset = (*env)->CallObjectMethod(env, hashMap, entrySetMethod);
356 //
357 //    // Get the Set Class
358 //    jclass jclass_of_set = (*env)->FindClass(env, "java/util/Set");
359 //
360 //    if (jclass_of_set == 0) {
361 //        printf("java/util/Set lookup failed\n");
362 //        return;
363 //    }
364 //
365 //    // Get link to Method "iterator"
366 //    jmethodID iteratorMethod = (*env)->GetMethodID(env, jclass_of_set, "iterator", "()Ljava/util/Iterator;");
367 //
368 //    // Invoke the "iterator" method on the jobject_of_entryset variable of type Set
369 //    jobject jobject_of_iterator = (*env)->CallObjectMethod(env, jobject_of_entryset, iteratorMethod);
370 //
371 //    // Get the "Iterator" class
372 //    jclass jclass_of_iterator = (*env)->FindClass(env, "java/util/Iterator");
373 //
374 //    // Get link to Method "hasNext"
375 //    jmethodID hasNextMethod = (*env)->GetMethodID(env, jclass_of_iterator, "hasNext", "()Z");
376 //
377 //    // Invoke - Get the value hasNextMethod
378 //    jboolean bHasNext = (*env)->CallBooleanMethod(env, jobject_of_iterator, hasNextMethod);
379 
380 //    // Get link to Method "hasNext"
381 //    jmethodID nextMethod = (*env)->GetMethodID(env, jclass_of_iterator, "next", "()Ljava/util/Map/Entry;");
382 //
383 //    jclass jclass_of_mapentry = (*env)->FindClass(env, "java/util/Map/Entry");
384 //
385 //    jmethodID getKeyMethod = (*env)->GetMethodID(env, jclass_of_mapentry, "getKey", "()Ljava/lang/Object");
386 //
387 //    jmethodID getValueMethod = (*env)->GetMethodID(env, jclass_of_mapentry, "getValue", "()Ljava/lang/Object");
388 }
389 
appendThumbnail(JNIEnv * env,jobject jobj,jstring jfilename,jstring jthumbnailfilename)390 static jboolean appendThumbnail(JNIEnv *env, jobject jobj, jstring jfilename, jstring jthumbnailfilename)
391 {
392 #ifdef SUPERDEBUG
393     LOGE("******************************** appendThumbnail\n");
394 #endif
395 
396     const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
397     if (filename == NULL) {
398         return JNI_FALSE;
399     }
400     const char* thumbnailfilename = (*env)->GetStringUTFChars(env, jthumbnailfilename, NULL);
401     if (thumbnailfilename == NULL) {
402         return JNI_FALSE;
403     }
404  #ifdef SUPERDEBUG
405      LOGE("*******before actual call to ReplaceThumbnail\n");
406      ShowImageInfo(TRUE);
407  #endif
408     ReplaceThumbnail(thumbnailfilename);
409  #ifdef SUPERDEBUG
410      ShowImageInfo(TRUE);
411  #endif
412     (*env)->ReleaseStringUTFChars(env, jfilename, filename);
413     (*env)->ReleaseStringUTFChars(env, jthumbnailfilename, thumbnailfilename);
414 
415     DiscardData();
416     return JNI_TRUE;
417 }
418 
commitChanges(JNIEnv * env,jobject jobj,jstring jfilename)419 static void commitChanges(JNIEnv *env, jobject jobj, jstring jfilename)
420 {
421 #ifdef SUPERDEBUG
422     LOGE("******************************** commitChanges\n");
423 #endif
424     const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
425     if (filename) {
426         saveJPGFile(filename);
427         DiscardData();
428         (*env)->ReleaseStringUTFChars(env, jfilename, filename);
429     }
430 }
431 
getThumbnail(JNIEnv * env,jobject jobj,jstring jfilename)432 static jbyteArray getThumbnail(JNIEnv *env, jobject jobj, jstring jfilename)
433 {
434 #ifdef SUPERDEBUG
435     LOGE("******************************** getThumbnail\n");
436 #endif
437 
438     const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
439     if (filename) {
440         loadExifInfo(filename, FALSE);
441         Section_t* ExifSection = FindSection(M_EXIF);
442         if (ExifSection == NULL ||  ImageInfo.ThumbnailSize == 0) {
443 #ifdef SUPERDEBUG
444     LOGE("no exif section or size == 0, so no thumbnail\n");
445 #endif
446             goto noThumbnail;
447         }
448         uchar* thumbnailPointer = ExifSection->Data + ImageInfo.ThumbnailOffset + 8;
449 
450         jbyteArray byteArray = (*env)->NewByteArray(env, ImageInfo.ThumbnailSize);
451         if (byteArray == NULL) {
452 #ifdef SUPERDEBUG
453     LOGE("couldn't allocate thumbnail memory, so no thumbnail\n");
454 #endif
455             goto noThumbnail;
456         }
457         (*env)->SetByteArrayRegion(env, byteArray, 0, ImageInfo.ThumbnailSize, thumbnailPointer);
458 #ifdef SUPERDEBUG
459     LOGE("thumbnail size %d\n", ImageInfo.ThumbnailSize);
460 #endif
461         (*env)->ReleaseStringUTFChars(env, jfilename, filename);
462         DiscardData();
463         return byteArray;
464     }
465 noThumbnail:
466     if (filename) {
467         (*env)->ReleaseStringUTFChars(env, jfilename, filename);
468     }
469     DiscardData();
470     return NULL;
471 }
472 
473 static int attributeCount;      // keep track of how many attributes we've added
474 
475 // returns new buffer length
addKeyValueString(char ** buf,int bufLen,const char * key,const char * value)476 static int addKeyValueString(char** buf, int bufLen, const char* key, const char* value) {
477     // Appends to buf like this: "ImageLength=4 1024"
478 
479     char valueLen[15];
480     snprintf(valueLen, 15, "=%d ", (int)strlen(value));
481 
482     // check to see if buf has enough room to append
483     int len = strlen(key) + strlen(valueLen) + strlen(value);
484     int newLen = strlen(*buf) + len;
485     if (newLen >= bufLen) {
486 #ifdef REALLOCTEST
487         bufLen = newLen + 5;
488         LOGE("reallocing to %d", bufLen);
489 #else
490         bufLen = newLen + 500;
491 #endif
492         *buf = realloc(*buf, bufLen);
493         if (*buf == NULL) {
494             return 0;
495         }
496     }
497     // append the new attribute and value
498     snprintf(*buf + strlen(*buf), bufLen, "%s%s%s", key, valueLen, value);
499 #ifdef SUPERDEBUG
500     LOGE("buf %s", *buf);
501 #endif
502     ++attributeCount;
503     return bufLen;
504 }
505 
506 // returns new buffer length
addKeyValueInt(char ** buf,int bufLen,const char * key,int value)507 static int addKeyValueInt(char** buf, int bufLen, const char* key, int value) {
508     char valueStr[20];
509     snprintf(valueStr, 20, "%d", value);
510 
511     return addKeyValueString(buf, bufLen, key, valueStr);
512 }
513 
514 // returns new buffer length
addKeyValueDouble(char ** buf,int bufLen,const char * key,double value,const char * format)515 static int addKeyValueDouble(char** buf, int bufLen, const char* key, double value, const char* format) {
516     char valueStr[30];
517     snprintf(valueStr, 30, format, value);
518 
519     return addKeyValueString(buf, bufLen, key, valueStr);
520 }
521 
522 // Returns new buffer length. Rational value will be appended as "numerator/denominator".
addKeyValueRational(char ** buf,int bufLen,const char * key,rat_t value)523 static int addKeyValueRational(char** buf, int bufLen, const char* key, rat_t value) {
524     char valueStr[25];
525     snprintf(valueStr, sizeof(valueStr), "%u/%u", value.num, value.denom);
526     return addKeyValueString(buf, bufLen, key, valueStr);
527 }
528 
getAttributes(JNIEnv * env,jobject jobj,jstring jfilename)529 static jstring getAttributes(JNIEnv *env, jobject jobj, jstring jfilename)
530 {
531 #ifdef SUPERDEBUG
532     LOGE("******************************** getAttributes\n");
533 #endif
534     const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
535     loadExifInfo(filename, FALSE);
536 #ifdef SUPERDEBUG
537     ShowImageInfo(TRUE);
538 #endif
539     (*env)->ReleaseStringUTFChars(env, jfilename, filename);
540 
541     attributeCount = 0;
542 #ifdef REALLOCTEST
543     int bufLen = 5;
544 #else
545     int bufLen = 1000;
546 #endif
547     char* buf = malloc(bufLen);
548     if (buf == NULL) {
549         return NULL;
550     }
551     *buf = 0;   // start the string out at zero length
552 
553     // save a fake "hasThumbnail" tag to pass to the java ExifInterface
554     bufLen = addKeyValueString(&buf, bufLen, "hasThumbnail",
555         ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailAtEnd == FALSE || ImageInfo.ThumbnailSize == 0 ?
556             "false" : "true");
557     if (bufLen == 0) return NULL;
558 
559     if (ImageInfo.CameraMake[0]) {
560         bufLen = addKeyValueString(&buf, bufLen, "Make", ImageInfo.CameraMake);
561         if (bufLen == 0) return NULL;
562     }
563     if (ImageInfo.CameraModel[0]) {
564         bufLen = addKeyValueString(&buf, bufLen, "Model", ImageInfo.CameraModel);
565         if (bufLen == 0) return NULL;
566     }
567     if (ImageInfo.DateTime[0]) {
568         bufLen = addKeyValueString(&buf, bufLen, "DateTime", ImageInfo.DateTime);
569         if (bufLen == 0) return NULL;
570     }
571     bufLen = addKeyValueInt(&buf, bufLen, "ImageWidth", ImageInfo.Width);
572     if (bufLen == 0) return NULL;
573 
574     bufLen = addKeyValueInt(&buf, bufLen, "ImageLength", ImageInfo.Height);
575     if (bufLen == 0) return NULL;
576 
577     bufLen = addKeyValueInt(&buf, bufLen, "Orientation", ImageInfo.Orientation);
578     if (bufLen == 0) return NULL;
579 
580     if (ImageInfo.FlashUsed >= 0) {
581         bufLen = addKeyValueInt(&buf, bufLen, "Flash", ImageInfo.FlashUsed);
582         if (bufLen == 0) return NULL;
583     }
584 
585     if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0) {
586         bufLen = addKeyValueRational(&buf, bufLen, "FocalLength", ImageInfo.FocalLength);
587         if (bufLen == 0) return NULL;
588     }
589 
590     if (ImageInfo.DigitalZoomRatio > 1.0){
591         // Digital zoom used.  Shame on you!
592         bufLen = addKeyValueDouble(&buf, bufLen, "DigitalZoomRatio", ImageInfo.DigitalZoomRatio, "%1.3f");
593         if (bufLen == 0) return NULL;
594     }
595 
596     if (ImageInfo.ExposureTime){
597         const char* format;
598         if (ImageInfo.ExposureTime < 0.010){
599             format = "%6.4f";
600         } else {
601             format = "%5.3f";
602         }
603 
604         bufLen = addKeyValueDouble(&buf, bufLen, "ExposureTime", (double)ImageInfo.ExposureTime, format);
605         if (bufLen == 0) return NULL;
606     }
607 
608     if (ImageInfo.ApertureFNumber){
609         bufLen = addKeyValueDouble(&buf, bufLen, "FNumber", (double)ImageInfo.ApertureFNumber, "%3.1f");
610         if (bufLen == 0) return NULL;
611     }
612 
613     if (ImageInfo.Distance){
614         bufLen = addKeyValueDouble(&buf, bufLen, "SubjectDistance", (double)ImageInfo.Distance, "%4.2f");
615         if (bufLen == 0) return NULL;
616     }
617 
618     if (ImageInfo.ISOequivalent){
619         bufLen = addKeyValueInt(&buf, bufLen, "ISOSpeedRatings", ImageInfo.ISOequivalent);
620         if (bufLen == 0) return NULL;
621     }
622 
623     if (ImageInfo.ExposureBias){
624         // If exposure bias was specified, but set to zero, presumably its no bias at all,
625         // so only show it if its nonzero.
626         bufLen = addKeyValueDouble(&buf, bufLen, "ExposureBiasValue", (double)ImageInfo.ExposureBias, "%4.2f");
627         if (bufLen == 0) return NULL;
628     }
629 
630     if (ImageInfo.Whitebalance >= 0) {
631         bufLen = addKeyValueInt(&buf, bufLen, "WhiteBalance", ImageInfo.Whitebalance);
632         if (bufLen == 0) return NULL;
633     }
634 
635     bufLen = addKeyValueInt(&buf, bufLen, "LightSource", ImageInfo.LightSource);
636     if (bufLen == 0) return NULL;
637 
638 
639     if (ImageInfo.MeteringMode) {
640         bufLen = addKeyValueInt(&buf, bufLen, "MeteringMode", ImageInfo.MeteringMode);
641         if (bufLen == 0) return NULL;
642     }
643 
644     if (ImageInfo.ExposureProgram) {
645         bufLen = addKeyValueInt(&buf, bufLen, "ExposureProgram", ImageInfo.ExposureProgram);
646         if (bufLen == 0) return NULL;
647     }
648 
649     if (ImageInfo.ExposureMode) {
650         bufLen = addKeyValueInt(&buf, bufLen, "ExposureMode", ImageInfo.ExposureMode);
651         if (bufLen == 0) return NULL;
652     }
653 
654     if (ImageInfo.GpsInfoPresent) {
655         if (ImageInfo.GpsLatRaw[0]) {
656             bufLen = addKeyValueString(&buf, bufLen, "GPSLatitude", ImageInfo.GpsLatRaw);
657             if (bufLen == 0) return NULL;
658         }
659         if (ImageInfo.GpsLatRef[0]) {
660             bufLen = addKeyValueString(&buf, bufLen, "GPSLatitudeRef", ImageInfo.GpsLatRef);
661             if (bufLen == 0) return NULL;
662         }
663         if (ImageInfo.GpsLongRaw[0]) {
664             bufLen = addKeyValueString(&buf, bufLen, "GPSLongitude", ImageInfo.GpsLongRaw);
665             if (bufLen == 0) return NULL;
666         }
667         if (ImageInfo.GpsLongRef[0]) {
668             bufLen = addKeyValueString(&buf, bufLen, "GPSLongitudeRef", ImageInfo.GpsLongRef);
669             if (bufLen == 0) return NULL;
670         }
671         if (ImageInfo.GpsAlt[0]) {
672             bufLen = addKeyValueRational(&buf, bufLen, "GPSAltitude", ImageInfo.GpsAltRaw);
673             bufLen = addKeyValueInt(&buf, bufLen, "GPSAltitudeRef", ImageInfo.GpsAltRef);
674             if (bufLen == 0) return NULL;
675         }
676         if (ImageInfo.GpsDateStamp[0]) {
677             bufLen = addKeyValueString(&buf, bufLen, "GPSDateStamp", ImageInfo.GpsDateStamp);
678             if (bufLen == 0) return NULL;
679         }
680         if (ImageInfo.GpsTimeStamp[0]) {
681             bufLen = addKeyValueString(&buf, bufLen, "GPSTimeStamp", ImageInfo.GpsTimeStamp);
682             if (bufLen == 0) return NULL;
683         }
684         if (ImageInfo.GpsProcessingMethod[0]) {
685             bufLen = addKeyValueString(&buf, bufLen, "GPSProcessingMethod", ImageInfo.GpsProcessingMethod);
686             if (bufLen == 0) return NULL;
687         }
688     }
689 
690     if (ImageInfo.Comments[0]) {
691         bufLen = addKeyValueString(&buf, bufLen, "UserComment", ImageInfo.Comments);
692         if (bufLen == 0) return NULL;
693     }
694 
695     // put the attribute count at the beginnnig of the string
696     int finalBufLen = strlen(buf) + 20;
697     char* finalResult = malloc(finalBufLen);
698     if (finalResult == NULL) {
699         free(buf);
700         return NULL;
701     }
702     snprintf(finalResult, finalBufLen, "%d %s", attributeCount, buf);
703     int k;
704     for (k = 0; k < finalBufLen; k++)
705         if (!isascii(finalResult[k]))
706             finalResult[k] = '?';
707     free(buf);
708 
709 #ifdef SUPERDEBUG
710     LOGE("*********Returning result \"%s\"", finalResult);
711 #endif
712     jstring result = ((*env)->NewStringUTF(env, finalResult));
713     free(finalResult);
714     DiscardData();
715     return result;
716 }
717 
718 static const char *classPathName = "android/media/ExifInterface";
719 
720 static JNINativeMethod methods[] = {
721   {"saveAttributesNative", "(Ljava/lang/String;Ljava/lang/String;)V", (void*)saveAttributes },
722   {"getAttributesNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getAttributes },
723   {"appendThumbnailNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)appendThumbnail },
724   {"commitChangesNative", "(Ljava/lang/String;)V", (void*)commitChanges },
725   {"getThumbnailNative", "(Ljava/lang/String;)[B", (void*)getThumbnail },
726 };
727 
728 /*
729  * Register several native methods for one class.
730  */
registerNativeMethods(JNIEnv * env,const char * className,JNINativeMethod * gMethods,int numMethods)731 static int registerNativeMethods(JNIEnv* env, const char* className,
732     JNINativeMethod* gMethods, int numMethods)
733 {
734     jclass clazz;
735 
736     clazz = (*env)->FindClass(env, className);
737     if (clazz == NULL) {
738         fprintf(stderr,
739             "Native registration unable to find class '%s'\n", className);
740         return JNI_FALSE;
741     }
742     if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
743         fprintf(stderr, "RegisterNatives failed for '%s'\n", className);
744         return JNI_FALSE;
745     }
746 
747     return JNI_TRUE;
748 }
749 
750 /*
751  * Register native methods for all classes we know about.
752  */
registerNatives(JNIEnv * env)753 static int registerNatives(JNIEnv* env)
754 {
755     return jniRegisterNativeMethods(env, classPathName,
756                                     methods, NELEM(methods));
757 }
758 
759 /*
760  * Set some test stuff up.
761  *
762  * Returns the JNI version on success, -1 on failure.
763  */
JNI_OnLoad(JavaVM * vm,void * reserved)764 __attribute__ ((visibility("default"))) jint JNI_OnLoad(JavaVM* vm, void* reserved)
765 {
766     JNIEnv* env = NULL;
767     jint result = -1;
768 
769     if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
770         fprintf(stderr, "ERROR: GetEnv failed\n");
771         goto bail;
772     }
773     assert(env != NULL);
774 
775     printf("In mgmain JNI_OnLoad\n");
776 
777     if (registerNatives(env) < 0) {
778         fprintf(stderr, "ERROR: Exif native registration failed\n");
779         goto bail;
780     }
781 
782     /* success -- return valid version number */
783     result = JNI_VERSION_1_4;
784 
785 bail:
786     return result;
787 }
788