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