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