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