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