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