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