1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "MtpDatabaseJNI" 18 #include "utils/Log.h" 19 20 #include "android_media_Utils.h" 21 #include "mtp.h" 22 #include "MtpDatabase.h" 23 #include "MtpDataPacket.h" 24 #include "MtpObjectInfo.h" 25 #include "MtpProperty.h" 26 #include "MtpStringBuffer.h" 27 #include "MtpUtils.h" 28 29 #include "src/piex_types.h" 30 #include "src/piex.h" 31 32 extern "C" { 33 #include "libexif/exif-content.h" 34 #include "libexif/exif-data.h" 35 #include "libexif/exif-tag.h" 36 #include "libexif/exif-utils.h" 37 } 38 39 #include <android_runtime/AndroidRuntime.h> 40 #include <android_runtime/Log.h> 41 #include <jni.h> 42 #include <JNIHelp.h> 43 #include <nativehelper/ScopedLocalRef.h> 44 45 #include <assert.h> 46 #include <fcntl.h> 47 #include <inttypes.h> 48 #include <limits.h> 49 #include <stdio.h> 50 #include <unistd.h> 51 52 using namespace android; 53 54 // ---------------------------------------------------------------------------- 55 56 static jmethodID method_beginSendObject; 57 static jmethodID method_endSendObject; 58 static jmethodID method_getObjectList; 59 static jmethodID method_getNumObjects; 60 static jmethodID method_getSupportedPlaybackFormats; 61 static jmethodID method_getSupportedCaptureFormats; 62 static jmethodID method_getSupportedObjectProperties; 63 static jmethodID method_getSupportedDeviceProperties; 64 static jmethodID method_setObjectProperty; 65 static jmethodID method_getDeviceProperty; 66 static jmethodID method_setDeviceProperty; 67 static jmethodID method_getObjectPropertyList; 68 static jmethodID method_getObjectInfo; 69 static jmethodID method_getObjectFilePath; 70 static jmethodID method_deleteFile; 71 static jmethodID method_getObjectReferences; 72 static jmethodID method_setObjectReferences; 73 static jmethodID method_sessionStarted; 74 static jmethodID method_sessionEnded; 75 76 static jfieldID field_context; 77 static jfieldID field_batteryLevel; 78 static jfieldID field_batteryScale; 79 80 // MtpPropertyList fields 81 static jfieldID field_mCount; 82 static jfieldID field_mResult; 83 static jfieldID field_mObjectHandles; 84 static jfieldID field_mPropertyCodes; 85 static jfieldID field_mDataTypes; 86 static jfieldID field_mLongValues; 87 static jfieldID field_mStringValues; 88 89 90 MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) { 91 return (MtpDatabase *)env->GetLongField(database, field_context); 92 } 93 94 // ---------------------------------------------------------------------------- 95 96 class MyMtpDatabase : public MtpDatabase { 97 private: 98 jobject mDatabase; 99 jintArray mIntBuffer; 100 jlongArray mLongBuffer; 101 jcharArray mStringBuffer; 102 103 public: 104 MyMtpDatabase(JNIEnv *env, jobject client); 105 virtual ~MyMtpDatabase(); 106 void cleanup(JNIEnv *env); 107 108 virtual MtpObjectHandle beginSendObject(const char* path, 109 MtpObjectFormat format, 110 MtpObjectHandle parent, 111 MtpStorageID storage, 112 uint64_t size, 113 time_t modified); 114 115 virtual void endSendObject(const char* path, 116 MtpObjectHandle handle, 117 MtpObjectFormat format, 118 bool succeeded); 119 120 virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID, 121 MtpObjectFormat format, 122 MtpObjectHandle parent); 123 124 virtual int getNumObjects(MtpStorageID storageID, 125 MtpObjectFormat format, 126 MtpObjectHandle parent); 127 128 // callee should delete[] the results from these 129 // results can be NULL 130 virtual MtpObjectFormatList* getSupportedPlaybackFormats(); 131 virtual MtpObjectFormatList* getSupportedCaptureFormats(); 132 virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat format); 133 virtual MtpDevicePropertyList* getSupportedDeviceProperties(); 134 135 virtual MtpResponseCode getObjectPropertyValue(MtpObjectHandle handle, 136 MtpObjectProperty property, 137 MtpDataPacket& packet); 138 139 virtual MtpResponseCode setObjectPropertyValue(MtpObjectHandle handle, 140 MtpObjectProperty property, 141 MtpDataPacket& packet); 142 143 virtual MtpResponseCode getDevicePropertyValue(MtpDeviceProperty property, 144 MtpDataPacket& packet); 145 146 virtual MtpResponseCode setDevicePropertyValue(MtpDeviceProperty property, 147 MtpDataPacket& packet); 148 149 virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty property); 150 151 virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle handle, 152 uint32_t format, uint32_t property, 153 int groupCode, int depth, 154 MtpDataPacket& packet); 155 156 virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, 157 MtpObjectInfo& info); 158 159 virtual void* getThumbnail(MtpObjectHandle handle, size_t& outThumbSize); 160 161 virtual MtpResponseCode getObjectFilePath(MtpObjectHandle handle, 162 MtpString& outFilePath, 163 int64_t& outFileLength, 164 MtpObjectFormat& outFormat); 165 virtual MtpResponseCode deleteFile(MtpObjectHandle handle); 166 167 bool getObjectPropertyInfo(MtpObjectProperty property, int& type); 168 bool getDevicePropertyInfo(MtpDeviceProperty property, int& type); 169 170 virtual MtpObjectHandleList* getObjectReferences(MtpObjectHandle handle); 171 172 virtual MtpResponseCode setObjectReferences(MtpObjectHandle handle, 173 MtpObjectHandleList* references); 174 175 virtual MtpProperty* getObjectPropertyDesc(MtpObjectProperty property, 176 MtpObjectFormat format); 177 178 virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty property); 179 180 virtual void sessionStarted(); 181 182 virtual void sessionEnded(); 183 }; 184 185 // ---------------------------------------------------------------------------- 186 187 static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { 188 if (env->ExceptionCheck()) { 189 ALOGE("An exception was thrown by callback '%s'.", methodName); 190 LOGE_EX(env); 191 env->ExceptionClear(); 192 } 193 } 194 195 // ---------------------------------------------------------------------------- 196 197 MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client) 198 : mDatabase(env->NewGlobalRef(client)), 199 mIntBuffer(NULL), 200 mLongBuffer(NULL), 201 mStringBuffer(NULL) 202 { 203 // create buffers for out arguments 204 // we don't need to be thread-safe so this is OK 205 jintArray intArray = env->NewIntArray(3); 206 if (!intArray) { 207 return; // Already threw. 208 } 209 mIntBuffer = (jintArray)env->NewGlobalRef(intArray); 210 jlongArray longArray = env->NewLongArray(2); 211 if (!longArray) { 212 return; // Already threw. 213 } 214 mLongBuffer = (jlongArray)env->NewGlobalRef(longArray); 215 // Needs to be long enough to hold a file path for getObjectFilePath() 216 jcharArray charArray = env->NewCharArray(PATH_MAX + 1); 217 if (!charArray) { 218 return; // Already threw. 219 } 220 mStringBuffer = (jcharArray)env->NewGlobalRef(charArray); 221 } 222 223 void MyMtpDatabase::cleanup(JNIEnv *env) { 224 env->DeleteGlobalRef(mDatabase); 225 env->DeleteGlobalRef(mIntBuffer); 226 env->DeleteGlobalRef(mLongBuffer); 227 env->DeleteGlobalRef(mStringBuffer); 228 } 229 230 MyMtpDatabase::~MyMtpDatabase() { 231 } 232 233 MtpObjectHandle MyMtpDatabase::beginSendObject(const char* path, 234 MtpObjectFormat format, 235 MtpObjectHandle parent, 236 MtpStorageID storage, 237 uint64_t size, 238 time_t modified) { 239 JNIEnv* env = AndroidRuntime::getJNIEnv(); 240 jstring pathStr = env->NewStringUTF(path); 241 MtpObjectHandle result = env->CallIntMethod(mDatabase, method_beginSendObject, 242 pathStr, (jint)format, (jint)parent, (jint)storage, 243 (jlong)size, (jlong)modified); 244 245 if (pathStr) 246 env->DeleteLocalRef(pathStr); 247 checkAndClearExceptionFromCallback(env, __FUNCTION__); 248 return result; 249 } 250 251 void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle, 252 MtpObjectFormat format, bool succeeded) { 253 JNIEnv* env = AndroidRuntime::getJNIEnv(); 254 jstring pathStr = env->NewStringUTF(path); 255 env->CallVoidMethod(mDatabase, method_endSendObject, pathStr, 256 (jint)handle, (jint)format, (jboolean)succeeded); 257 258 if (pathStr) 259 env->DeleteLocalRef(pathStr); 260 checkAndClearExceptionFromCallback(env, __FUNCTION__); 261 } 262 263 MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID, 264 MtpObjectFormat format, 265 MtpObjectHandle parent) { 266 JNIEnv* env = AndroidRuntime::getJNIEnv(); 267 jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectList, 268 (jint)storageID, (jint)format, (jint)parent); 269 if (!array) 270 return NULL; 271 MtpObjectHandleList* list = new MtpObjectHandleList(); 272 jint* handles = env->GetIntArrayElements(array, 0); 273 jsize length = env->GetArrayLength(array); 274 for (int i = 0; i < length; i++) 275 list->push(handles[i]); 276 env->ReleaseIntArrayElements(array, handles, 0); 277 env->DeleteLocalRef(array); 278 279 checkAndClearExceptionFromCallback(env, __FUNCTION__); 280 return list; 281 } 282 283 int MyMtpDatabase::getNumObjects(MtpStorageID storageID, 284 MtpObjectFormat format, 285 MtpObjectHandle parent) { 286 JNIEnv* env = AndroidRuntime::getJNIEnv(); 287 int result = env->CallIntMethod(mDatabase, method_getNumObjects, 288 (jint)storageID, (jint)format, (jint)parent); 289 290 checkAndClearExceptionFromCallback(env, __FUNCTION__); 291 return result; 292 } 293 294 MtpObjectFormatList* MyMtpDatabase::getSupportedPlaybackFormats() { 295 JNIEnv* env = AndroidRuntime::getJNIEnv(); 296 jintArray array = (jintArray)env->CallObjectMethod(mDatabase, 297 method_getSupportedPlaybackFormats); 298 if (!array) 299 return NULL; 300 MtpObjectFormatList* list = new MtpObjectFormatList(); 301 jint* formats = env->GetIntArrayElements(array, 0); 302 jsize length = env->GetArrayLength(array); 303 for (int i = 0; i < length; i++) 304 list->push(formats[i]); 305 env->ReleaseIntArrayElements(array, formats, 0); 306 env->DeleteLocalRef(array); 307 308 checkAndClearExceptionFromCallback(env, __FUNCTION__); 309 return list; 310 } 311 312 MtpObjectFormatList* MyMtpDatabase::getSupportedCaptureFormats() { 313 JNIEnv* env = AndroidRuntime::getJNIEnv(); 314 jintArray array = (jintArray)env->CallObjectMethod(mDatabase, 315 method_getSupportedCaptureFormats); 316 if (!array) 317 return NULL; 318 MtpObjectFormatList* list = new MtpObjectFormatList(); 319 jint* formats = env->GetIntArrayElements(array, 0); 320 jsize length = env->GetArrayLength(array); 321 for (int i = 0; i < length; i++) 322 list->push(formats[i]); 323 env->ReleaseIntArrayElements(array, formats, 0); 324 env->DeleteLocalRef(array); 325 326 checkAndClearExceptionFromCallback(env, __FUNCTION__); 327 return list; 328 } 329 330 MtpObjectPropertyList* MyMtpDatabase::getSupportedObjectProperties(MtpObjectFormat format) { 331 JNIEnv* env = AndroidRuntime::getJNIEnv(); 332 jintArray array = (jintArray)env->CallObjectMethod(mDatabase, 333 method_getSupportedObjectProperties, (jint)format); 334 if (!array) 335 return NULL; 336 MtpObjectPropertyList* list = new MtpObjectPropertyList(); 337 jint* properties = env->GetIntArrayElements(array, 0); 338 jsize length = env->GetArrayLength(array); 339 for (int i = 0; i < length; i++) 340 list->push(properties[i]); 341 env->ReleaseIntArrayElements(array, properties, 0); 342 env->DeleteLocalRef(array); 343 344 checkAndClearExceptionFromCallback(env, __FUNCTION__); 345 return list; 346 } 347 348 MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() { 349 JNIEnv* env = AndroidRuntime::getJNIEnv(); 350 jintArray array = (jintArray)env->CallObjectMethod(mDatabase, 351 method_getSupportedDeviceProperties); 352 if (!array) 353 return NULL; 354 MtpDevicePropertyList* list = new MtpDevicePropertyList(); 355 jint* properties = env->GetIntArrayElements(array, 0); 356 jsize length = env->GetArrayLength(array); 357 for (int i = 0; i < length; i++) 358 list->push(properties[i]); 359 env->ReleaseIntArrayElements(array, properties, 0); 360 env->DeleteLocalRef(array); 361 362 checkAndClearExceptionFromCallback(env, __FUNCTION__); 363 return list; 364 } 365 366 MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle, 367 MtpObjectProperty property, 368 MtpDataPacket& packet) { 369 static_assert(sizeof(jint) >= sizeof(MtpObjectHandle), 370 "Casting MtpObjectHandle to jint loses a value"); 371 static_assert(sizeof(jint) >= sizeof(MtpObjectProperty), 372 "Casting MtpObjectProperty to jint loses a value"); 373 JNIEnv* env = AndroidRuntime::getJNIEnv(); 374 jobject list = env->CallObjectMethod( 375 mDatabase, 376 method_getObjectPropertyList, 377 static_cast<jint>(handle), 378 0, 379 static_cast<jint>(property), 380 0, 381 0); 382 MtpResponseCode result = env->GetIntField(list, field_mResult); 383 int count = env->GetIntField(list, field_mCount); 384 if (result == MTP_RESPONSE_OK && count != 1) 385 result = MTP_RESPONSE_GENERAL_ERROR; 386 387 if (result == MTP_RESPONSE_OK) { 388 jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles); 389 jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes); 390 jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes); 391 jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues); 392 jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues); 393 394 jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0); 395 jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0); 396 jint* dataTypes = env->GetIntArrayElements(dataTypesArray, 0); 397 jlong* longValues = (longValuesArray ? env->GetLongArrayElements(longValuesArray, 0) : NULL); 398 399 int type = dataTypes[0]; 400 jlong longValue = (longValues ? longValues[0] : 0); 401 402 // special case date properties, which are strings to MTP 403 // but stored internally as a uint64 404 if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) { 405 char date[20]; 406 formatDateTime(longValue, date, sizeof(date)); 407 packet.putString(date); 408 goto out; 409 } 410 // release date is stored internally as just the year 411 if (property == MTP_PROPERTY_ORIGINAL_RELEASE_DATE) { 412 char date[20]; 413 snprintf(date, sizeof(date), "%04" PRId64 "0101T000000", longValue); 414 packet.putString(date); 415 goto out; 416 } 417 418 switch (type) { 419 case MTP_TYPE_INT8: 420 packet.putInt8(longValue); 421 break; 422 case MTP_TYPE_UINT8: 423 packet.putUInt8(longValue); 424 break; 425 case MTP_TYPE_INT16: 426 packet.putInt16(longValue); 427 break; 428 case MTP_TYPE_UINT16: 429 packet.putUInt16(longValue); 430 break; 431 case MTP_TYPE_INT32: 432 packet.putInt32(longValue); 433 break; 434 case MTP_TYPE_UINT32: 435 packet.putUInt32(longValue); 436 break; 437 case MTP_TYPE_INT64: 438 packet.putInt64(longValue); 439 break; 440 case MTP_TYPE_UINT64: 441 packet.putUInt64(longValue); 442 break; 443 case MTP_TYPE_INT128: 444 packet.putInt128(longValue); 445 break; 446 case MTP_TYPE_UINT128: 447 packet.putInt128(longValue); 448 break; 449 case MTP_TYPE_STR: 450 { 451 jstring stringValue = (jstring)env->GetObjectArrayElement(stringValuesArray, 0); 452 const char* str = (stringValue ? env->GetStringUTFChars(stringValue, NULL) : NULL); 453 if (stringValue) { 454 packet.putString(str); 455 env->ReleaseStringUTFChars(stringValue, str); 456 } else { 457 packet.putEmptyString(); 458 } 459 env->DeleteLocalRef(stringValue); 460 break; 461 } 462 default: 463 ALOGE("unsupported type in getObjectPropertyValue\n"); 464 result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT; 465 } 466 out: 467 env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0); 468 env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0); 469 env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0); 470 if (longValues) 471 env->ReleaseLongArrayElements(longValuesArray, longValues, 0); 472 473 env->DeleteLocalRef(objectHandlesArray); 474 env->DeleteLocalRef(propertyCodesArray); 475 env->DeleteLocalRef(dataTypesArray); 476 if (longValuesArray) 477 env->DeleteLocalRef(longValuesArray); 478 if (stringValuesArray) 479 env->DeleteLocalRef(stringValuesArray); 480 } 481 482 env->DeleteLocalRef(list); 483 checkAndClearExceptionFromCallback(env, __FUNCTION__); 484 return result; 485 } 486 487 static bool readLongValue(int type, MtpDataPacket& packet, jlong& longValue) { 488 switch (type) { 489 case MTP_TYPE_INT8: { 490 int8_t temp; 491 if (!packet.getInt8(temp)) return false; 492 longValue = temp; 493 break; 494 } 495 case MTP_TYPE_UINT8: { 496 uint8_t temp; 497 if (!packet.getUInt8(temp)) return false; 498 longValue = temp; 499 break; 500 } 501 case MTP_TYPE_INT16: { 502 int16_t temp; 503 if (!packet.getInt16(temp)) return false; 504 longValue = temp; 505 break; 506 } 507 case MTP_TYPE_UINT16: { 508 uint16_t temp; 509 if (!packet.getUInt16(temp)) return false; 510 longValue = temp; 511 break; 512 } 513 case MTP_TYPE_INT32: { 514 int32_t temp; 515 if (!packet.getInt32(temp)) return false; 516 longValue = temp; 517 break; 518 } 519 case MTP_TYPE_UINT32: { 520 uint32_t temp; 521 if (!packet.getUInt32(temp)) return false; 522 longValue = temp; 523 break; 524 } 525 case MTP_TYPE_INT64: { 526 int64_t temp; 527 if (!packet.getInt64(temp)) return false; 528 longValue = temp; 529 break; 530 } 531 case MTP_TYPE_UINT64: { 532 uint64_t temp; 533 if (!packet.getUInt64(temp)) return false; 534 longValue = temp; 535 break; 536 } 537 default: 538 ALOGE("unsupported type in readLongValue"); 539 return false; 540 } 541 return true; 542 } 543 544 MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle, 545 MtpObjectProperty property, 546 MtpDataPacket& packet) { 547 int type; 548 549 if (!getObjectPropertyInfo(property, type)) 550 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED; 551 552 JNIEnv* env = AndroidRuntime::getJNIEnv(); 553 jlong longValue = 0; 554 jstring stringValue = NULL; 555 MtpResponseCode result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT; 556 557 if (type == MTP_TYPE_STR) { 558 MtpStringBuffer buffer; 559 if (!packet.getString(buffer)) goto fail; 560 stringValue = env->NewStringUTF((const char *)buffer); 561 } else { 562 if (!readLongValue(type, packet, longValue)) goto fail; 563 } 564 565 result = env->CallIntMethod(mDatabase, method_setObjectProperty, 566 (jint)handle, (jint)property, longValue, stringValue); 567 if (stringValue) 568 env->DeleteLocalRef(stringValue); 569 570 fail: 571 checkAndClearExceptionFromCallback(env, __FUNCTION__); 572 return result; 573 } 574 575 MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property, 576 MtpDataPacket& packet) { 577 JNIEnv* env = AndroidRuntime::getJNIEnv(); 578 579 if (property == MTP_DEVICE_PROPERTY_BATTERY_LEVEL) { 580 // special case - implemented here instead of Java 581 packet.putUInt8((uint8_t)env->GetIntField(mDatabase, field_batteryLevel)); 582 return MTP_RESPONSE_OK; 583 } else { 584 int type; 585 586 if (!getDevicePropertyInfo(property, type)) 587 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; 588 589 jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty, 590 (jint)property, mLongBuffer, mStringBuffer); 591 if (result != MTP_RESPONSE_OK) { 592 checkAndClearExceptionFromCallback(env, __FUNCTION__); 593 return result; 594 } 595 596 jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0); 597 jlong longValue = longValues[0]; 598 env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); 599 600 switch (type) { 601 case MTP_TYPE_INT8: 602 packet.putInt8(longValue); 603 break; 604 case MTP_TYPE_UINT8: 605 packet.putUInt8(longValue); 606 break; 607 case MTP_TYPE_INT16: 608 packet.putInt16(longValue); 609 break; 610 case MTP_TYPE_UINT16: 611 packet.putUInt16(longValue); 612 break; 613 case MTP_TYPE_INT32: 614 packet.putInt32(longValue); 615 break; 616 case MTP_TYPE_UINT32: 617 packet.putUInt32(longValue); 618 break; 619 case MTP_TYPE_INT64: 620 packet.putInt64(longValue); 621 break; 622 case MTP_TYPE_UINT64: 623 packet.putUInt64(longValue); 624 break; 625 case MTP_TYPE_INT128: 626 packet.putInt128(longValue); 627 break; 628 case MTP_TYPE_UINT128: 629 packet.putInt128(longValue); 630 break; 631 case MTP_TYPE_STR: 632 { 633 jchar* str = env->GetCharArrayElements(mStringBuffer, 0); 634 packet.putString(str); 635 env->ReleaseCharArrayElements(mStringBuffer, str, 0); 636 break; 637 } 638 default: 639 ALOGE("unsupported type in getDevicePropertyValue\n"); 640 return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT; 641 } 642 643 checkAndClearExceptionFromCallback(env, __FUNCTION__); 644 return MTP_RESPONSE_OK; 645 } 646 } 647 648 MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property, 649 MtpDataPacket& packet) { 650 int type; 651 652 if (!getDevicePropertyInfo(property, type)) 653 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; 654 655 JNIEnv* env = AndroidRuntime::getJNIEnv(); 656 jlong longValue = 0; 657 jstring stringValue = NULL; 658 MtpResponseCode result = MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT; 659 660 if (type == MTP_TYPE_STR) { 661 MtpStringBuffer buffer; 662 if (!packet.getString(buffer)) goto fail; 663 stringValue = env->NewStringUTF((const char *)buffer); 664 } else { 665 if (!readLongValue(type, packet, longValue)) goto fail; 666 } 667 668 result = env->CallIntMethod(mDatabase, method_setDeviceProperty, 669 (jint)property, longValue, stringValue); 670 if (stringValue) 671 env->DeleteLocalRef(stringValue); 672 673 fail: 674 checkAndClearExceptionFromCallback(env, __FUNCTION__); 675 return result; 676 } 677 678 MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty /*property*/) { 679 return -1; 680 } 681 682 MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle, 683 uint32_t format, uint32_t property, 684 int groupCode, int depth, 685 MtpDataPacket& packet) { 686 static_assert(sizeof(jint) >= sizeof(MtpObjectHandle), 687 "Casting MtpObjectHandle to jint loses a value"); 688 JNIEnv* env = AndroidRuntime::getJNIEnv(); 689 jobject list = env->CallObjectMethod( 690 mDatabase, 691 method_getObjectPropertyList, 692 static_cast<jint>(handle), 693 static_cast<jint>(format), 694 static_cast<jint>(property), 695 static_cast<jint>(groupCode), 696 static_cast<jint>(depth)); 697 checkAndClearExceptionFromCallback(env, __FUNCTION__); 698 if (!list) 699 return MTP_RESPONSE_GENERAL_ERROR; 700 int count = env->GetIntField(list, field_mCount); 701 MtpResponseCode result = env->GetIntField(list, field_mResult); 702 703 packet.putUInt32(count); 704 if (count > 0) { 705 jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles); 706 jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes); 707 jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes); 708 jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues); 709 jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues); 710 711 jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0); 712 jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0); 713 jint* dataTypes = env->GetIntArrayElements(dataTypesArray, 0); 714 jlong* longValues = (longValuesArray ? env->GetLongArrayElements(longValuesArray, 0) : NULL); 715 716 for (int i = 0; i < count; i++) { 717 packet.putUInt32(objectHandles[i]); 718 packet.putUInt16(propertyCodes[i]); 719 int type = dataTypes[i]; 720 packet.putUInt16(type); 721 722 switch (type) { 723 case MTP_TYPE_INT8: 724 packet.putInt8(longValues[i]); 725 break; 726 case MTP_TYPE_UINT8: 727 packet.putUInt8(longValues[i]); 728 break; 729 case MTP_TYPE_INT16: 730 packet.putInt16(longValues[i]); 731 break; 732 case MTP_TYPE_UINT16: 733 packet.putUInt16(longValues[i]); 734 break; 735 case MTP_TYPE_INT32: 736 packet.putInt32(longValues[i]); 737 break; 738 case MTP_TYPE_UINT32: 739 packet.putUInt32(longValues[i]); 740 break; 741 case MTP_TYPE_INT64: 742 packet.putInt64(longValues[i]); 743 break; 744 case MTP_TYPE_UINT64: 745 packet.putUInt64(longValues[i]); 746 break; 747 case MTP_TYPE_INT128: 748 packet.putInt128(longValues[i]); 749 break; 750 case MTP_TYPE_UINT128: 751 packet.putUInt128(longValues[i]); 752 break; 753 case MTP_TYPE_STR: { 754 jstring value = (jstring)env->GetObjectArrayElement(stringValuesArray, i); 755 const char *valueStr = (value ? env->GetStringUTFChars(value, NULL) : NULL); 756 if (valueStr) { 757 packet.putString(valueStr); 758 env->ReleaseStringUTFChars(value, valueStr); 759 } else { 760 packet.putEmptyString(); 761 } 762 env->DeleteLocalRef(value); 763 break; 764 } 765 default: 766 ALOGE("bad or unsupported data type in MyMtpDatabase::getObjectPropertyList"); 767 break; 768 } 769 } 770 771 env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0); 772 env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0); 773 env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0); 774 if (longValues) 775 env->ReleaseLongArrayElements(longValuesArray, longValues, 0); 776 777 env->DeleteLocalRef(objectHandlesArray); 778 env->DeleteLocalRef(propertyCodesArray); 779 env->DeleteLocalRef(dataTypesArray); 780 if (longValuesArray) 781 env->DeleteLocalRef(longValuesArray); 782 if (stringValuesArray) 783 env->DeleteLocalRef(stringValuesArray); 784 } 785 786 env->DeleteLocalRef(list); 787 checkAndClearExceptionFromCallback(env, __FUNCTION__); 788 return result; 789 } 790 791 static void foreachentry(ExifEntry *entry, void* /* user */) { 792 char buf[1024]; 793 ALOGI("entry %x, format %d, size %d: %s", 794 entry->tag, entry->format, entry->size, exif_entry_get_value(entry, buf, sizeof(buf))); 795 } 796 797 static void foreachcontent(ExifContent *content, void *user) { 798 ALOGI("content %d", exif_content_get_ifd(content)); 799 exif_content_foreach_entry(content, foreachentry, user); 800 } 801 802 static long getLongFromExifEntry(ExifEntry *e) { 803 ExifByteOrder o = exif_data_get_byte_order(e->parent->parent); 804 return exif_get_long(e->data, o); 805 } 806 807 MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle, 808 MtpObjectInfo& info) { 809 MtpString path; 810 int64_t length; 811 MtpObjectFormat format; 812 813 MtpResponseCode result = getObjectFilePath(handle, path, length, format); 814 if (result != MTP_RESPONSE_OK) { 815 return result; 816 } 817 info.mCompressedSize = (length > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)length); 818 819 JNIEnv* env = AndroidRuntime::getJNIEnv(); 820 if (!env->CallBooleanMethod(mDatabase, method_getObjectInfo, 821 (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer)) { 822 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 823 } 824 825 jint* intValues = env->GetIntArrayElements(mIntBuffer, 0); 826 info.mStorageID = intValues[0]; 827 info.mFormat = intValues[1]; 828 info.mParent = intValues[2]; 829 env->ReleaseIntArrayElements(mIntBuffer, intValues, 0); 830 831 jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0); 832 info.mDateCreated = longValues[0]; 833 info.mDateModified = longValues[1]; 834 env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); 835 836 if ((false)) { 837 info.mAssociationType = (format == MTP_FORMAT_ASSOCIATION ? 838 MTP_ASSOCIATION_TYPE_GENERIC_FOLDER : 839 MTP_ASSOCIATION_TYPE_UNDEFINED); 840 } 841 info.mAssociationType = MTP_ASSOCIATION_TYPE_UNDEFINED; 842 843 jchar* str = env->GetCharArrayElements(mStringBuffer, 0); 844 MtpString temp(reinterpret_cast<char16_t*>(str)); 845 info.mName = strdup((const char *)temp); 846 env->ReleaseCharArrayElements(mStringBuffer, str, 0); 847 848 // read EXIF data for thumbnail information 849 switch (info.mFormat) { 850 case MTP_FORMAT_EXIF_JPEG: 851 case MTP_FORMAT_JFIF: { 852 ExifData *exifdata = exif_data_new_from_file(path); 853 if (exifdata) { 854 if ((false)) { 855 exif_data_foreach_content(exifdata, foreachcontent, NULL); 856 } 857 858 ExifEntry *w = exif_content_get_entry( 859 exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_X_DIMENSION); 860 ExifEntry *h = exif_content_get_entry( 861 exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_Y_DIMENSION); 862 info.mThumbCompressedSize = exifdata->data ? exifdata->size : 0; 863 info.mThumbFormat = MTP_FORMAT_EXIF_JPEG; 864 info.mImagePixWidth = w ? getLongFromExifEntry(w) : 0; 865 info.mImagePixHeight = h ? getLongFromExifEntry(h) : 0; 866 exif_data_unref(exifdata); 867 } 868 break; 869 } 870 871 // Except DNG, all supported RAW image formats are not defined in PTP 1.2 specification. 872 // Most of RAW image formats are based on TIFF or TIFF/EP. To render Fuji's RAF format, 873 // it checks MTP_FORMAT_DEFINED case since it's designed as a custom format. 874 case MTP_FORMAT_DNG: 875 case MTP_FORMAT_TIFF: 876 case MTP_FORMAT_TIFF_EP: 877 case MTP_FORMAT_DEFINED: { 878 std::unique_ptr<FileStream> stream(new FileStream(path)); 879 piex::PreviewImageData image_data; 880 if (!GetExifFromRawImage(stream.get(), path, image_data)) { 881 // Couldn't parse EXIF data from a image file via piex. 882 break; 883 } 884 885 info.mThumbCompressedSize = image_data.thumbnail.length; 886 info.mThumbFormat = MTP_FORMAT_EXIF_JPEG; 887 info.mImagePixWidth = image_data.full_width; 888 info.mImagePixHeight = image_data.full_height; 889 890 break; 891 } 892 } 893 894 checkAndClearExceptionFromCallback(env, __FUNCTION__); 895 return MTP_RESPONSE_OK; 896 } 897 898 void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) { 899 MtpString path; 900 int64_t length; 901 MtpObjectFormat format; 902 void* result = NULL; 903 outThumbSize = 0; 904 905 if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK) { 906 switch (format) { 907 case MTP_FORMAT_EXIF_JPEG: 908 case MTP_FORMAT_JFIF: { 909 ExifData *exifdata = exif_data_new_from_file(path); 910 if (exifdata) { 911 if (exifdata->data) { 912 result = malloc(exifdata->size); 913 if (result) { 914 memcpy(result, exifdata->data, exifdata->size); 915 outThumbSize = exifdata->size; 916 } 917 } 918 exif_data_unref(exifdata); 919 } 920 break; 921 } 922 923 // See the above comment on getObjectInfo() method. 924 case MTP_FORMAT_DNG: 925 case MTP_FORMAT_TIFF: 926 case MTP_FORMAT_TIFF_EP: 927 case MTP_FORMAT_DEFINED: { 928 std::unique_ptr<FileStream> stream(new FileStream(path)); 929 piex::PreviewImageData image_data; 930 if (!GetExifFromRawImage(stream.get(), path, image_data)) { 931 // Couldn't parse EXIF data from a image file via piex. 932 break; 933 } 934 935 if (image_data.thumbnail.length == 0 936 || image_data.thumbnail.format != ::piex::Image::kJpegCompressed) { 937 // No thumbnail or non jpeg thumbnail. 938 break; 939 } 940 941 result = malloc(image_data.thumbnail.length); 942 if (result) { 943 piex::Error err = stream.get()->GetData( 944 image_data.thumbnail.offset, 945 image_data.thumbnail.length, 946 (std::uint8_t *)result); 947 if (err == piex::Error::kOk) { 948 outThumbSize = image_data.thumbnail.length; 949 } else { 950 free(result); 951 } 952 } 953 break; 954 } 955 } 956 } 957 958 return result; 959 } 960 961 MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle, 962 MtpString& outFilePath, 963 int64_t& outFileLength, 964 MtpObjectFormat& outFormat) { 965 JNIEnv* env = AndroidRuntime::getJNIEnv(); 966 jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath, 967 (jint)handle, mStringBuffer, mLongBuffer); 968 if (result != MTP_RESPONSE_OK) { 969 checkAndClearExceptionFromCallback(env, __FUNCTION__); 970 return result; 971 } 972 973 jchar* str = env->GetCharArrayElements(mStringBuffer, 0); 974 outFilePath.setTo(reinterpret_cast<char16_t*>(str), 975 strlen16(reinterpret_cast<char16_t*>(str))); 976 env->ReleaseCharArrayElements(mStringBuffer, str, 0); 977 978 jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0); 979 outFileLength = longValues[0]; 980 outFormat = longValues[1]; 981 env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); 982 983 checkAndClearExceptionFromCallback(env, __FUNCTION__); 984 return result; 985 } 986 987 MtpResponseCode MyMtpDatabase::deleteFile(MtpObjectHandle handle) { 988 JNIEnv* env = AndroidRuntime::getJNIEnv(); 989 MtpResponseCode result = env->CallIntMethod(mDatabase, method_deleteFile, (jint)handle); 990 991 checkAndClearExceptionFromCallback(env, __FUNCTION__); 992 return result; 993 } 994 995 struct PropertyTableEntry { 996 MtpObjectProperty property; 997 int type; 998 }; 999 1000 static const PropertyTableEntry kObjectPropertyTable[] = { 1001 { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32 }, 1002 { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16 }, 1003 { MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16 }, 1004 { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64 }, 1005 { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR }, 1006 { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR }, 1007 { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 }, 1008 { MTP_PROPERTY_PERSISTENT_UID, MTP_TYPE_UINT128 }, 1009 { MTP_PROPERTY_NAME, MTP_TYPE_STR }, 1010 { MTP_PROPERTY_DISPLAY_NAME, MTP_TYPE_STR }, 1011 { MTP_PROPERTY_DATE_ADDED, MTP_TYPE_STR }, 1012 { MTP_PROPERTY_ARTIST, MTP_TYPE_STR }, 1013 { MTP_PROPERTY_ALBUM_NAME, MTP_TYPE_STR }, 1014 { MTP_PROPERTY_ALBUM_ARTIST, MTP_TYPE_STR }, 1015 { MTP_PROPERTY_TRACK, MTP_TYPE_UINT16 }, 1016 { MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_TYPE_STR }, 1017 { MTP_PROPERTY_GENRE, MTP_TYPE_STR }, 1018 { MTP_PROPERTY_COMPOSER, MTP_TYPE_STR }, 1019 { MTP_PROPERTY_DURATION, MTP_TYPE_UINT32 }, 1020 { MTP_PROPERTY_DESCRIPTION, MTP_TYPE_STR }, 1021 { MTP_PROPERTY_AUDIO_WAVE_CODEC, MTP_TYPE_UINT32 }, 1022 { MTP_PROPERTY_BITRATE_TYPE, MTP_TYPE_UINT16 }, 1023 { MTP_PROPERTY_AUDIO_BITRATE, MTP_TYPE_UINT32 }, 1024 { MTP_PROPERTY_NUMBER_OF_CHANNELS,MTP_TYPE_UINT16 }, 1025 { MTP_PROPERTY_SAMPLE_RATE, MTP_TYPE_UINT32 }, 1026 }; 1027 1028 static const PropertyTableEntry kDevicePropertyTable[] = { 1029 { MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER, MTP_TYPE_STR }, 1030 { MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME, MTP_TYPE_STR }, 1031 { MTP_DEVICE_PROPERTY_IMAGE_SIZE, MTP_TYPE_STR }, 1032 { MTP_DEVICE_PROPERTY_BATTERY_LEVEL, MTP_TYPE_UINT8 }, 1033 }; 1034 1035 bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) { 1036 int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]); 1037 const PropertyTableEntry* entry = kObjectPropertyTable; 1038 for (int i = 0; i < count; i++, entry++) { 1039 if (entry->property == property) { 1040 type = entry->type; 1041 return true; 1042 } 1043 } 1044 return false; 1045 } 1046 1047 bool MyMtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) { 1048 int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]); 1049 const PropertyTableEntry* entry = kDevicePropertyTable; 1050 for (int i = 0; i < count; i++, entry++) { 1051 if (entry->property == property) { 1052 type = entry->type; 1053 return true; 1054 } 1055 } 1056 return false; 1057 } 1058 1059 MtpObjectHandleList* MyMtpDatabase::getObjectReferences(MtpObjectHandle handle) { 1060 JNIEnv* env = AndroidRuntime::getJNIEnv(); 1061 jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectReferences, 1062 (jint)handle); 1063 if (!array) 1064 return NULL; 1065 MtpObjectHandleList* list = new MtpObjectHandleList(); 1066 jint* handles = env->GetIntArrayElements(array, 0); 1067 jsize length = env->GetArrayLength(array); 1068 for (int i = 0; i < length; i++) 1069 list->push(handles[i]); 1070 env->ReleaseIntArrayElements(array, handles, 0); 1071 env->DeleteLocalRef(array); 1072 1073 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1074 return list; 1075 } 1076 1077 MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle, 1078 MtpObjectHandleList* references) { 1079 JNIEnv* env = AndroidRuntime::getJNIEnv(); 1080 int count = references->size(); 1081 jintArray array = env->NewIntArray(count); 1082 if (!array) { 1083 ALOGE("out of memory in setObjectReferences"); 1084 return false; 1085 } 1086 jint* handles = env->GetIntArrayElements(array, 0); 1087 for (int i = 0; i < count; i++) 1088 handles[i] = (*references)[i]; 1089 env->ReleaseIntArrayElements(array, handles, 0); 1090 MtpResponseCode result = env->CallIntMethod(mDatabase, method_setObjectReferences, 1091 (jint)handle, array); 1092 env->DeleteLocalRef(array); 1093 1094 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1095 return result; 1096 } 1097 1098 MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property, 1099 MtpObjectFormat format) { 1100 static const int channelEnum[] = { 1101 1, // mono 1102 2, // stereo 1103 3, // 2.1 1104 4, // 3 1105 5, // 3.1 1106 6, // 4 1107 7, // 4.1 1108 8, // 5 1109 9, // 5.1 1110 }; 1111 static const int bitrateEnum[] = { 1112 1, // fixed rate 1113 2, // variable rate 1114 }; 1115 1116 MtpProperty* result = NULL; 1117 switch (property) { 1118 case MTP_PROPERTY_OBJECT_FORMAT: 1119 // use format as default value 1120 result = new MtpProperty(property, MTP_TYPE_UINT16, false, format); 1121 break; 1122 case MTP_PROPERTY_PROTECTION_STATUS: 1123 case MTP_PROPERTY_TRACK: 1124 result = new MtpProperty(property, MTP_TYPE_UINT16); 1125 break; 1126 case MTP_PROPERTY_STORAGE_ID: 1127 case MTP_PROPERTY_PARENT_OBJECT: 1128 case MTP_PROPERTY_DURATION: 1129 case MTP_PROPERTY_AUDIO_WAVE_CODEC: 1130 result = new MtpProperty(property, MTP_TYPE_UINT32); 1131 break; 1132 case MTP_PROPERTY_OBJECT_SIZE: 1133 result = new MtpProperty(property, MTP_TYPE_UINT64); 1134 break; 1135 case MTP_PROPERTY_PERSISTENT_UID: 1136 result = new MtpProperty(property, MTP_TYPE_UINT128); 1137 break; 1138 case MTP_PROPERTY_NAME: 1139 case MTP_PROPERTY_DISPLAY_NAME: 1140 case MTP_PROPERTY_ARTIST: 1141 case MTP_PROPERTY_ALBUM_NAME: 1142 case MTP_PROPERTY_ALBUM_ARTIST: 1143 case MTP_PROPERTY_GENRE: 1144 case MTP_PROPERTY_COMPOSER: 1145 case MTP_PROPERTY_DESCRIPTION: 1146 result = new MtpProperty(property, MTP_TYPE_STR); 1147 break; 1148 case MTP_PROPERTY_DATE_MODIFIED: 1149 case MTP_PROPERTY_DATE_ADDED: 1150 case MTP_PROPERTY_ORIGINAL_RELEASE_DATE: 1151 result = new MtpProperty(property, MTP_TYPE_STR); 1152 result->setFormDateTime(); 1153 break; 1154 case MTP_PROPERTY_OBJECT_FILE_NAME: 1155 // We allow renaming files and folders 1156 result = new MtpProperty(property, MTP_TYPE_STR, true); 1157 break; 1158 case MTP_PROPERTY_BITRATE_TYPE: 1159 result = new MtpProperty(property, MTP_TYPE_UINT16); 1160 result->setFormEnum(bitrateEnum, sizeof(bitrateEnum)/sizeof(bitrateEnum[0])); 1161 break; 1162 case MTP_PROPERTY_AUDIO_BITRATE: 1163 result = new MtpProperty(property, MTP_TYPE_UINT32); 1164 result->setFormRange(1, 1536000, 1); 1165 break; 1166 case MTP_PROPERTY_NUMBER_OF_CHANNELS: 1167 result = new MtpProperty(property, MTP_TYPE_UINT16); 1168 result->setFormEnum(channelEnum, sizeof(channelEnum)/sizeof(channelEnum[0])); 1169 break; 1170 case MTP_PROPERTY_SAMPLE_RATE: 1171 result = new MtpProperty(property, MTP_TYPE_UINT32); 1172 result->setFormRange(8000, 48000, 1); 1173 break; 1174 } 1175 1176 return result; 1177 } 1178 1179 MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) { 1180 JNIEnv* env = AndroidRuntime::getJNIEnv(); 1181 MtpProperty* result = NULL; 1182 bool writable = false; 1183 1184 switch (property) { 1185 case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER: 1186 case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME: 1187 writable = true; 1188 // fall through 1189 case MTP_DEVICE_PROPERTY_IMAGE_SIZE: { 1190 result = new MtpProperty(property, MTP_TYPE_STR, writable); 1191 1192 // get current value 1193 jint ret = env->CallIntMethod(mDatabase, method_getDeviceProperty, 1194 (jint)property, mLongBuffer, mStringBuffer); 1195 if (ret == MTP_RESPONSE_OK) { 1196 jchar* str = env->GetCharArrayElements(mStringBuffer, 0); 1197 result->setCurrentValue(str); 1198 // for read-only properties it is safe to assume current value is default value 1199 if (!writable) 1200 result->setDefaultValue(str); 1201 env->ReleaseCharArrayElements(mStringBuffer, str, 0); 1202 } else { 1203 ALOGE("unable to read device property, response: %04X", ret); 1204 } 1205 break; 1206 } 1207 case MTP_DEVICE_PROPERTY_BATTERY_LEVEL: 1208 result = new MtpProperty(property, MTP_TYPE_UINT8); 1209 result->setFormRange(0, env->GetIntField(mDatabase, field_batteryScale), 1); 1210 result->mCurrentValue.u.u8 = (uint8_t)env->GetIntField(mDatabase, field_batteryLevel); 1211 break; 1212 } 1213 1214 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1215 return result; 1216 } 1217 1218 void MyMtpDatabase::sessionStarted() { 1219 JNIEnv* env = AndroidRuntime::getJNIEnv(); 1220 env->CallVoidMethod(mDatabase, method_sessionStarted); 1221 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1222 } 1223 1224 void MyMtpDatabase::sessionEnded() { 1225 JNIEnv* env = AndroidRuntime::getJNIEnv(); 1226 env->CallVoidMethod(mDatabase, method_sessionEnded); 1227 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1228 } 1229 1230 // ---------------------------------------------------------------------------- 1231 1232 static void 1233 android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz) 1234 { 1235 MyMtpDatabase* database = new MyMtpDatabase(env, thiz); 1236 env->SetLongField(thiz, field_context, (jlong)database); 1237 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1238 } 1239 1240 static void 1241 android_mtp_MtpDatabase_finalize(JNIEnv *env, jobject thiz) 1242 { 1243 MyMtpDatabase* database = (MyMtpDatabase *)env->GetLongField(thiz, field_context); 1244 database->cleanup(env); 1245 delete database; 1246 env->SetLongField(thiz, field_context, 0); 1247 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1248 } 1249 1250 static jstring 1251 android_mtp_MtpPropertyGroup_format_date_time(JNIEnv *env, jobject /*thiz*/, jlong seconds) 1252 { 1253 char date[20]; 1254 formatDateTime(seconds, date, sizeof(date)); 1255 return env->NewStringUTF(date); 1256 } 1257 1258 // ---------------------------------------------------------------------------- 1259 1260 static const JNINativeMethod gMtpDatabaseMethods[] = { 1261 {"native_setup", "()V", (void *)android_mtp_MtpDatabase_setup}, 1262 {"native_finalize", "()V", (void *)android_mtp_MtpDatabase_finalize}, 1263 }; 1264 1265 static const JNINativeMethod gMtpPropertyGroupMethods[] = { 1266 {"format_date_time", "(J)Ljava/lang/String;", 1267 (void *)android_mtp_MtpPropertyGroup_format_date_time}, 1268 }; 1269 1270 int register_android_mtp_MtpDatabase(JNIEnv *env) 1271 { 1272 jclass clazz; 1273 1274 clazz = env->FindClass("android/mtp/MtpDatabase"); 1275 if (clazz == NULL) { 1276 ALOGE("Can't find android/mtp/MtpDatabase"); 1277 return -1; 1278 } 1279 method_beginSendObject = env->GetMethodID(clazz, "beginSendObject", "(Ljava/lang/String;IIIJJ)I"); 1280 if (method_beginSendObject == NULL) { 1281 ALOGE("Can't find beginSendObject"); 1282 return -1; 1283 } 1284 method_endSendObject = env->GetMethodID(clazz, "endSendObject", "(Ljava/lang/String;IIZ)V"); 1285 if (method_endSendObject == NULL) { 1286 ALOGE("Can't find endSendObject"); 1287 return -1; 1288 } 1289 method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I"); 1290 if (method_getObjectList == NULL) { 1291 ALOGE("Can't find getObjectList"); 1292 return -1; 1293 } 1294 method_getNumObjects = env->GetMethodID(clazz, "getNumObjects", "(III)I"); 1295 if (method_getNumObjects == NULL) { 1296 ALOGE("Can't find getNumObjects"); 1297 return -1; 1298 } 1299 method_getSupportedPlaybackFormats = env->GetMethodID(clazz, "getSupportedPlaybackFormats", "()[I"); 1300 if (method_getSupportedPlaybackFormats == NULL) { 1301 ALOGE("Can't find getSupportedPlaybackFormats"); 1302 return -1; 1303 } 1304 method_getSupportedCaptureFormats = env->GetMethodID(clazz, "getSupportedCaptureFormats", "()[I"); 1305 if (method_getSupportedCaptureFormats == NULL) { 1306 ALOGE("Can't find getSupportedCaptureFormats"); 1307 return -1; 1308 } 1309 method_getSupportedObjectProperties = env->GetMethodID(clazz, "getSupportedObjectProperties", "(I)[I"); 1310 if (method_getSupportedObjectProperties == NULL) { 1311 ALOGE("Can't find getSupportedObjectProperties"); 1312 return -1; 1313 } 1314 method_getSupportedDeviceProperties = env->GetMethodID(clazz, "getSupportedDeviceProperties", "()[I"); 1315 if (method_getSupportedDeviceProperties == NULL) { 1316 ALOGE("Can't find getSupportedDeviceProperties"); 1317 return -1; 1318 } 1319 method_setObjectProperty = env->GetMethodID(clazz, "setObjectProperty", "(IIJLjava/lang/String;)I"); 1320 if (method_setObjectProperty == NULL) { 1321 ALOGE("Can't find setObjectProperty"); 1322 return -1; 1323 } 1324 method_getDeviceProperty = env->GetMethodID(clazz, "getDeviceProperty", "(I[J[C)I"); 1325 if (method_getDeviceProperty == NULL) { 1326 ALOGE("Can't find getDeviceProperty"); 1327 return -1; 1328 } 1329 method_setDeviceProperty = env->GetMethodID(clazz, "setDeviceProperty", "(IJLjava/lang/String;)I"); 1330 if (method_setDeviceProperty == NULL) { 1331 ALOGE("Can't find setDeviceProperty"); 1332 return -1; 1333 } 1334 method_getObjectPropertyList = env->GetMethodID(clazz, "getObjectPropertyList", 1335 "(IIIII)Landroid/mtp/MtpPropertyList;"); 1336 if (method_getObjectPropertyList == NULL) { 1337 ALOGE("Can't find getObjectPropertyList"); 1338 return -1; 1339 } 1340 method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z"); 1341 if (method_getObjectInfo == NULL) { 1342 ALOGE("Can't find getObjectInfo"); 1343 return -1; 1344 } 1345 method_getObjectFilePath = env->GetMethodID(clazz, "getObjectFilePath", "(I[C[J)I"); 1346 if (method_getObjectFilePath == NULL) { 1347 ALOGE("Can't find getObjectFilePath"); 1348 return -1; 1349 } 1350 method_deleteFile = env->GetMethodID(clazz, "deleteFile", "(I)I"); 1351 if (method_deleteFile == NULL) { 1352 ALOGE("Can't find deleteFile"); 1353 return -1; 1354 } 1355 method_getObjectReferences = env->GetMethodID(clazz, "getObjectReferences", "(I)[I"); 1356 if (method_getObjectReferences == NULL) { 1357 ALOGE("Can't find getObjectReferences"); 1358 return -1; 1359 } 1360 method_setObjectReferences = env->GetMethodID(clazz, "setObjectReferences", "(I[I)I"); 1361 if (method_setObjectReferences == NULL) { 1362 ALOGE("Can't find setObjectReferences"); 1363 return -1; 1364 } 1365 method_sessionStarted = env->GetMethodID(clazz, "sessionStarted", "()V"); 1366 if (method_sessionStarted == NULL) { 1367 ALOGE("Can't find sessionStarted"); 1368 return -1; 1369 } 1370 method_sessionEnded = env->GetMethodID(clazz, "sessionEnded", "()V"); 1371 if (method_sessionEnded == NULL) { 1372 ALOGE("Can't find sessionEnded"); 1373 return -1; 1374 } 1375 1376 field_context = env->GetFieldID(clazz, "mNativeContext", "J"); 1377 if (field_context == NULL) { 1378 ALOGE("Can't find MtpDatabase.mNativeContext"); 1379 return -1; 1380 } 1381 field_batteryLevel = env->GetFieldID(clazz, "mBatteryLevel", "I"); 1382 if (field_batteryLevel == NULL) { 1383 ALOGE("Can't find MtpDatabase.mBatteryLevel"); 1384 return -1; 1385 } 1386 field_batteryScale = env->GetFieldID(clazz, "mBatteryScale", "I"); 1387 if (field_batteryScale == NULL) { 1388 ALOGE("Can't find MtpDatabase.mBatteryScale"); 1389 return -1; 1390 } 1391 1392 // now set up fields for MtpPropertyList class 1393 clazz = env->FindClass("android/mtp/MtpPropertyList"); 1394 if (clazz == NULL) { 1395 ALOGE("Can't find android/mtp/MtpPropertyList"); 1396 return -1; 1397 } 1398 field_mCount = env->GetFieldID(clazz, "mCount", "I"); 1399 if (field_mCount == NULL) { 1400 ALOGE("Can't find MtpPropertyList.mCount"); 1401 return -1; 1402 } 1403 field_mResult = env->GetFieldID(clazz, "mResult", "I"); 1404 if (field_mResult == NULL) { 1405 ALOGE("Can't find MtpPropertyList.mResult"); 1406 return -1; 1407 } 1408 field_mObjectHandles = env->GetFieldID(clazz, "mObjectHandles", "[I"); 1409 if (field_mObjectHandles == NULL) { 1410 ALOGE("Can't find MtpPropertyList.mObjectHandles"); 1411 return -1; 1412 } 1413 field_mPropertyCodes = env->GetFieldID(clazz, "mPropertyCodes", "[I"); 1414 if (field_mPropertyCodes == NULL) { 1415 ALOGE("Can't find MtpPropertyList.mPropertyCodes"); 1416 return -1; 1417 } 1418 field_mDataTypes = env->GetFieldID(clazz, "mDataTypes", "[I"); 1419 if (field_mDataTypes == NULL) { 1420 ALOGE("Can't find MtpPropertyList.mDataTypes"); 1421 return -1; 1422 } 1423 field_mLongValues = env->GetFieldID(clazz, "mLongValues", "[J"); 1424 if (field_mLongValues == NULL) { 1425 ALOGE("Can't find MtpPropertyList.mLongValues"); 1426 return -1; 1427 } 1428 field_mStringValues = env->GetFieldID(clazz, "mStringValues", "[Ljava/lang/String;"); 1429 if (field_mStringValues == NULL) { 1430 ALOGE("Can't find MtpPropertyList.mStringValues"); 1431 return -1; 1432 } 1433 1434 if (AndroidRuntime::registerNativeMethods(env, 1435 "android/mtp/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods))) 1436 return -1; 1437 1438 return AndroidRuntime::registerNativeMethods(env, 1439 "android/mtp/MtpPropertyGroup", gMtpPropertyGroupMethods, NELEM(gMtpPropertyGroupMethods)); 1440 } 1441