main.c revision 6a3cf8f158c6f0f7e069f8aeb0825a3fefeabd02
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 <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
57static 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
67extern void ResetJpgfile();
68
69static 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
107static 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
168void 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
194static 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
323exit:
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
389static 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
418static 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
431static 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    }
463noThumbnail:
464    if (filename) {
465        (*env)->ReleaseStringUTFChars(env, jfilename, filename);
466    }
467    DiscardData();
468    return NULL;
469}
470
471static int attributeCount;      // keep track of how many attributes we've added
472
473// returns new buffer length
474static 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
505static 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
513static 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
520static 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
684static const char *classPathName = "android/media/ExifInterface";
685
686static 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 */
697static 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 */
719static 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 */
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
751bail:
752    return result;
753}
754