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