1/*
2
3Copyright (c) 2008, The Android Open Source Project
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions
8are 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
19THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30SUCH 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
58static 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
68extern void ResetJpgfile();
69
70static 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
108static 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
169void 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
195static 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
329exit:
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
395static 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
424static 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
437static 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    }
471noThumbnail:
472    if (filename) {
473        (*env)->ReleaseStringUTFChars(env, jfilename, filename);
474    }
475    DiscardData();
476    return NULL;
477}
478
479static 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    }
500done:
501    if (filename) {
502        (*env)->ReleaseStringUTFChars(env, jfilename, filename);
503    }
504    DiscardData();
505    return resultArray;
506}
507
508static int attributeCount;      // keep track of how many attributes we've added
509
510// returns new buffer length
511static 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
542static 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
550static 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".
558static 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
564static 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
770static const char *classPathName = "android/media/ExifInterface";
771
772static 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 */
784static 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 */
806static 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 */
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
838bail:
839    return result;
840}
841