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