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