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 <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_JFIF: { 853 ExifData *exifdata = exif_data_new_from_file(path); 854 if (exifdata) { 855 if ((false)) { 856 exif_data_foreach_content(exifdata, foreachcontent, NULL); 857 } 858 859 ExifEntry *w = exif_content_get_entry( 860 exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_X_DIMENSION); 861 ExifEntry *h = exif_content_get_entry( 862 exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_Y_DIMENSION); 863 info.mThumbCompressedSize = exifdata->data ? exifdata->size : 0; 864 info.mThumbFormat = MTP_FORMAT_EXIF_JPEG; 865 info.mImagePixWidth = w ? getLongFromExifEntry(w) : 0; 866 info.mImagePixHeight = h ? getLongFromExifEntry(h) : 0; 867 exif_data_unref(exifdata); 868 } 869 break; 870 } 871 872 // Except DNG, all supported RAW image formats are not defined in PTP 1.2 specification. 873 // Most of RAW image formats are based on TIFF or TIFF/EP. To render Fuji's RAF format, 874 // it checks MTP_FORMAT_DEFINED case since it's designed as a custom format. 875 case MTP_FORMAT_DNG: 876 case MTP_FORMAT_TIFF: 877 case MTP_FORMAT_TIFF_EP: 878 case MTP_FORMAT_DEFINED: { 879 std::unique_ptr<FileStream> stream(new FileStream(path)); 880 piex::PreviewImageData image_data; 881 if (!GetExifFromRawImage(stream.get(), path, image_data)) { 882 // Couldn't parse EXIF data from a image file via piex. 883 break; 884 } 885 886 info.mThumbCompressedSize = image_data.thumbnail.length; 887 info.mThumbFormat = MTP_FORMAT_EXIF_JPEG; 888 info.mImagePixWidth = image_data.full_width; 889 info.mImagePixHeight = image_data.full_height; 890 891 break; 892 } 893 } 894 895 checkAndClearExceptionFromCallback(env, __FUNCTION__); 896 return MTP_RESPONSE_OK; 897} 898 899void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) { 900 MtpString path; 901 int64_t length; 902 MtpObjectFormat format; 903 void* result = NULL; 904 outThumbSize = 0; 905 906 if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK) { 907 switch (format) { 908 case MTP_FORMAT_EXIF_JPEG: 909 case MTP_FORMAT_JFIF: { 910 ExifData *exifdata = exif_data_new_from_file(path); 911 if (exifdata) { 912 if (exifdata->data) { 913 result = malloc(exifdata->size); 914 if (result) { 915 memcpy(result, exifdata->data, exifdata->size); 916 outThumbSize = exifdata->size; 917 } 918 } 919 exif_data_unref(exifdata); 920 } 921 break; 922 } 923 924 // See the above comment on getObjectInfo() method. 925 case MTP_FORMAT_DNG: 926 case MTP_FORMAT_TIFF: 927 case MTP_FORMAT_TIFF_EP: 928 case MTP_FORMAT_DEFINED: { 929 std::unique_ptr<FileStream> stream(new FileStream(path)); 930 piex::PreviewImageData image_data; 931 if (!GetExifFromRawImage(stream.get(), path, image_data)) { 932 // Couldn't parse EXIF data from a image file via piex. 933 break; 934 } 935 936 if (image_data.thumbnail.length == 0 937 || image_data.thumbnail.format != ::piex::Image::kJpegCompressed) { 938 // No thumbnail or non jpeg thumbnail. 939 break; 940 } 941 942 result = malloc(image_data.thumbnail.length); 943 if (result) { 944 piex::Error err = stream.get()->GetData( 945 image_data.thumbnail.offset, 946 image_data.thumbnail.length, 947 (std::uint8_t *)result); 948 if (err == piex::Error::kOk) { 949 outThumbSize = image_data.thumbnail.length; 950 } else { 951 free(result); 952 } 953 } 954 break; 955 } 956 } 957 } 958 959 return result; 960} 961 962MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle, 963 MtpString& outFilePath, 964 int64_t& outFileLength, 965 MtpObjectFormat& outFormat) { 966 JNIEnv* env = AndroidRuntime::getJNIEnv(); 967 jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath, 968 (jint)handle, mStringBuffer, mLongBuffer); 969 if (result != MTP_RESPONSE_OK) { 970 checkAndClearExceptionFromCallback(env, __FUNCTION__); 971 return result; 972 } 973 974 jchar* str = env->GetCharArrayElements(mStringBuffer, 0); 975 outFilePath.setTo(reinterpret_cast<char16_t*>(str), 976 strlen16(reinterpret_cast<char16_t*>(str))); 977 env->ReleaseCharArrayElements(mStringBuffer, str, 0); 978 979 jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0); 980 outFileLength = longValues[0]; 981 outFormat = longValues[1]; 982 env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); 983 984 checkAndClearExceptionFromCallback(env, __FUNCTION__); 985 return result; 986} 987 988MtpResponseCode MyMtpDatabase::deleteFile(MtpObjectHandle handle) { 989 JNIEnv* env = AndroidRuntime::getJNIEnv(); 990 MtpResponseCode result = env->CallIntMethod(mDatabase, method_deleteFile, (jint)handle); 991 992 checkAndClearExceptionFromCallback(env, __FUNCTION__); 993 return result; 994} 995 996struct PropertyTableEntry { 997 MtpObjectProperty property; 998 int type; 999}; 1000 1001static const PropertyTableEntry kObjectPropertyTable[] = { 1002 { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32 }, 1003 { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16 }, 1004 { MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16 }, 1005 { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64 }, 1006 { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR }, 1007 { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR }, 1008 { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 }, 1009 { MTP_PROPERTY_PERSISTENT_UID, MTP_TYPE_UINT128 }, 1010 { MTP_PROPERTY_NAME, MTP_TYPE_STR }, 1011 { MTP_PROPERTY_DISPLAY_NAME, MTP_TYPE_STR }, 1012 { MTP_PROPERTY_DATE_ADDED, MTP_TYPE_STR }, 1013 { MTP_PROPERTY_ARTIST, MTP_TYPE_STR }, 1014 { MTP_PROPERTY_ALBUM_NAME, MTP_TYPE_STR }, 1015 { MTP_PROPERTY_ALBUM_ARTIST, MTP_TYPE_STR }, 1016 { MTP_PROPERTY_TRACK, MTP_TYPE_UINT16 }, 1017 { MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_TYPE_STR }, 1018 { MTP_PROPERTY_GENRE, MTP_TYPE_STR }, 1019 { MTP_PROPERTY_COMPOSER, MTP_TYPE_STR }, 1020 { MTP_PROPERTY_DURATION, MTP_TYPE_UINT32 }, 1021 { MTP_PROPERTY_DESCRIPTION, MTP_TYPE_STR }, 1022 { MTP_PROPERTY_AUDIO_WAVE_CODEC, MTP_TYPE_UINT32 }, 1023 { MTP_PROPERTY_BITRATE_TYPE, MTP_TYPE_UINT16 }, 1024 { MTP_PROPERTY_AUDIO_BITRATE, MTP_TYPE_UINT32 }, 1025 { MTP_PROPERTY_NUMBER_OF_CHANNELS,MTP_TYPE_UINT16 }, 1026 { MTP_PROPERTY_SAMPLE_RATE, MTP_TYPE_UINT32 }, 1027}; 1028 1029static const PropertyTableEntry kDevicePropertyTable[] = { 1030 { MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER, MTP_TYPE_STR }, 1031 { MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME, MTP_TYPE_STR }, 1032 { MTP_DEVICE_PROPERTY_IMAGE_SIZE, MTP_TYPE_STR }, 1033 { MTP_DEVICE_PROPERTY_BATTERY_LEVEL, MTP_TYPE_UINT8 }, 1034 { MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE, MTP_TYPE_UINT32 }, 1035}; 1036 1037bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) { 1038 int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]); 1039 const PropertyTableEntry* entry = kObjectPropertyTable; 1040 for (int i = 0; i < count; i++, entry++) { 1041 if (entry->property == property) { 1042 type = entry->type; 1043 return true; 1044 } 1045 } 1046 return false; 1047} 1048 1049bool MyMtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) { 1050 int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]); 1051 const PropertyTableEntry* entry = kDevicePropertyTable; 1052 for (int i = 0; i < count; i++, entry++) { 1053 if (entry->property == property) { 1054 type = entry->type; 1055 return true; 1056 } 1057 } 1058 return false; 1059} 1060 1061MtpObjectHandleList* MyMtpDatabase::getObjectReferences(MtpObjectHandle handle) { 1062 JNIEnv* env = AndroidRuntime::getJNIEnv(); 1063 jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectReferences, 1064 (jint)handle); 1065 if (!array) 1066 return NULL; 1067 MtpObjectHandleList* list = new MtpObjectHandleList(); 1068 jint* handles = env->GetIntArrayElements(array, 0); 1069 jsize length = env->GetArrayLength(array); 1070 for (int i = 0; i < length; i++) 1071 list->push(handles[i]); 1072 env->ReleaseIntArrayElements(array, handles, 0); 1073 env->DeleteLocalRef(array); 1074 1075 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1076 return list; 1077} 1078 1079MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle, 1080 MtpObjectHandleList* references) { 1081 JNIEnv* env = AndroidRuntime::getJNIEnv(); 1082 int count = references->size(); 1083 jintArray array = env->NewIntArray(count); 1084 if (!array) { 1085 ALOGE("out of memory in setObjectReferences"); 1086 return false; 1087 } 1088 jint* handles = env->GetIntArrayElements(array, 0); 1089 for (int i = 0; i < count; i++) 1090 handles[i] = (*references)[i]; 1091 env->ReleaseIntArrayElements(array, handles, 0); 1092 MtpResponseCode result = env->CallIntMethod(mDatabase, method_setObjectReferences, 1093 (jint)handle, array); 1094 env->DeleteLocalRef(array); 1095 1096 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1097 return result; 1098} 1099 1100MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property, 1101 MtpObjectFormat format) { 1102 static const int channelEnum[] = { 1103 1, // mono 1104 2, // stereo 1105 3, // 2.1 1106 4, // 3 1107 5, // 3.1 1108 6, // 4 1109 7, // 4.1 1110 8, // 5 1111 9, // 5.1 1112 }; 1113 static const int bitrateEnum[] = { 1114 1, // fixed rate 1115 2, // variable rate 1116 }; 1117 1118 MtpProperty* result = NULL; 1119 switch (property) { 1120 case MTP_PROPERTY_OBJECT_FORMAT: 1121 // use format as default value 1122 result = new MtpProperty(property, MTP_TYPE_UINT16, false, format); 1123 break; 1124 case MTP_PROPERTY_PROTECTION_STATUS: 1125 case MTP_PROPERTY_TRACK: 1126 result = new MtpProperty(property, MTP_TYPE_UINT16); 1127 break; 1128 case MTP_PROPERTY_STORAGE_ID: 1129 case MTP_PROPERTY_PARENT_OBJECT: 1130 case MTP_PROPERTY_DURATION: 1131 case MTP_PROPERTY_AUDIO_WAVE_CODEC: 1132 result = new MtpProperty(property, MTP_TYPE_UINT32); 1133 break; 1134 case MTP_PROPERTY_OBJECT_SIZE: 1135 result = new MtpProperty(property, MTP_TYPE_UINT64); 1136 break; 1137 case MTP_PROPERTY_PERSISTENT_UID: 1138 result = new MtpProperty(property, MTP_TYPE_UINT128); 1139 break; 1140 case MTP_PROPERTY_NAME: 1141 case MTP_PROPERTY_DISPLAY_NAME: 1142 case MTP_PROPERTY_ARTIST: 1143 case MTP_PROPERTY_ALBUM_NAME: 1144 case MTP_PROPERTY_ALBUM_ARTIST: 1145 case MTP_PROPERTY_GENRE: 1146 case MTP_PROPERTY_COMPOSER: 1147 case MTP_PROPERTY_DESCRIPTION: 1148 result = new MtpProperty(property, MTP_TYPE_STR); 1149 break; 1150 case MTP_PROPERTY_DATE_MODIFIED: 1151 case MTP_PROPERTY_DATE_ADDED: 1152 case MTP_PROPERTY_ORIGINAL_RELEASE_DATE: 1153 result = new MtpProperty(property, MTP_TYPE_STR); 1154 result->setFormDateTime(); 1155 break; 1156 case MTP_PROPERTY_OBJECT_FILE_NAME: 1157 // We allow renaming files and folders 1158 result = new MtpProperty(property, MTP_TYPE_STR, true); 1159 break; 1160 case MTP_PROPERTY_BITRATE_TYPE: 1161 result = new MtpProperty(property, MTP_TYPE_UINT16); 1162 result->setFormEnum(bitrateEnum, sizeof(bitrateEnum)/sizeof(bitrateEnum[0])); 1163 break; 1164 case MTP_PROPERTY_AUDIO_BITRATE: 1165 result = new MtpProperty(property, MTP_TYPE_UINT32); 1166 result->setFormRange(1, 1536000, 1); 1167 break; 1168 case MTP_PROPERTY_NUMBER_OF_CHANNELS: 1169 result = new MtpProperty(property, MTP_TYPE_UINT16); 1170 result->setFormEnum(channelEnum, sizeof(channelEnum)/sizeof(channelEnum[0])); 1171 break; 1172 case MTP_PROPERTY_SAMPLE_RATE: 1173 result = new MtpProperty(property, MTP_TYPE_UINT32); 1174 result->setFormRange(8000, 48000, 1); 1175 break; 1176 } 1177 1178 return result; 1179} 1180 1181MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) { 1182 JNIEnv* env = AndroidRuntime::getJNIEnv(); 1183 MtpProperty* result = NULL; 1184 bool writable = false; 1185 1186 switch (property) { 1187 case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER: 1188 case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME: 1189 writable = true; 1190 // fall through 1191 case MTP_DEVICE_PROPERTY_IMAGE_SIZE: { 1192 result = new MtpProperty(property, MTP_TYPE_STR, writable); 1193 1194 // get current value 1195 jint ret = env->CallIntMethod(mDatabase, method_getDeviceProperty, 1196 (jint)property, mLongBuffer, mStringBuffer); 1197 if (ret == MTP_RESPONSE_OK) { 1198 jchar* str = env->GetCharArrayElements(mStringBuffer, 0); 1199 result->setCurrentValue(str); 1200 // for read-only properties it is safe to assume current value is default value 1201 if (!writable) 1202 result->setDefaultValue(str); 1203 env->ReleaseCharArrayElements(mStringBuffer, str, 0); 1204 } else { 1205 ALOGE("unable to read device property, response: %04X", ret); 1206 } 1207 break; 1208 } 1209 case MTP_DEVICE_PROPERTY_BATTERY_LEVEL: 1210 result = new MtpProperty(property, MTP_TYPE_UINT8); 1211 result->setFormRange(0, env->GetIntField(mDatabase, field_batteryScale), 1); 1212 result->mCurrentValue.u.u8 = (uint8_t)env->GetIntField(mDatabase, field_batteryLevel); 1213 break; 1214 case MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE: 1215 result = new MtpProperty(property, MTP_TYPE_UINT32); 1216 result->mCurrentValue.u.u32 = (uint32_t)env->GetIntField(mDatabase, field_deviceType); 1217 break; 1218 } 1219 1220 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1221 return result; 1222} 1223 1224void MyMtpDatabase::sessionStarted() { 1225 JNIEnv* env = AndroidRuntime::getJNIEnv(); 1226 env->CallVoidMethod(mDatabase, method_sessionStarted); 1227 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1228} 1229 1230void MyMtpDatabase::sessionEnded() { 1231 JNIEnv* env = AndroidRuntime::getJNIEnv(); 1232 env->CallVoidMethod(mDatabase, method_sessionEnded); 1233 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1234} 1235 1236// ---------------------------------------------------------------------------- 1237 1238static void 1239android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz) 1240{ 1241 MyMtpDatabase* database = new MyMtpDatabase(env, thiz); 1242 env->SetLongField(thiz, field_context, (jlong)database); 1243 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1244} 1245 1246static void 1247android_mtp_MtpDatabase_finalize(JNIEnv *env, jobject thiz) 1248{ 1249 MyMtpDatabase* database = (MyMtpDatabase *)env->GetLongField(thiz, field_context); 1250 database->cleanup(env); 1251 delete database; 1252 env->SetLongField(thiz, field_context, 0); 1253 checkAndClearExceptionFromCallback(env, __FUNCTION__); 1254} 1255 1256static jstring 1257android_mtp_MtpPropertyGroup_format_date_time(JNIEnv *env, jobject /*thiz*/, jlong seconds) 1258{ 1259 char date[20]; 1260 formatDateTime(seconds, date, sizeof(date)); 1261 return env->NewStringUTF(date); 1262} 1263 1264// ---------------------------------------------------------------------------- 1265 1266static const JNINativeMethod gMtpDatabaseMethods[] = { 1267 {"native_setup", "()V", (void *)android_mtp_MtpDatabase_setup}, 1268 {"native_finalize", "()V", (void *)android_mtp_MtpDatabase_finalize}, 1269}; 1270 1271static const JNINativeMethod gMtpPropertyGroupMethods[] = { 1272 {"format_date_time", "(J)Ljava/lang/String;", 1273 (void *)android_mtp_MtpPropertyGroup_format_date_time}, 1274}; 1275 1276int register_android_mtp_MtpDatabase(JNIEnv *env) 1277{ 1278 jclass clazz; 1279 1280 clazz = env->FindClass("android/mtp/MtpDatabase"); 1281 if (clazz == NULL) { 1282 ALOGE("Can't find android/mtp/MtpDatabase"); 1283 return -1; 1284 } 1285 method_beginSendObject = env->GetMethodID(clazz, "beginSendObject", "(Ljava/lang/String;IIIJJ)I"); 1286 if (method_beginSendObject == NULL) { 1287 ALOGE("Can't find beginSendObject"); 1288 return -1; 1289 } 1290 method_endSendObject = env->GetMethodID(clazz, "endSendObject", "(Ljava/lang/String;IIZ)V"); 1291 if (method_endSendObject == NULL) { 1292 ALOGE("Can't find endSendObject"); 1293 return -1; 1294 } 1295 method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I"); 1296 if (method_getObjectList == NULL) { 1297 ALOGE("Can't find getObjectList"); 1298 return -1; 1299 } 1300 method_getNumObjects = env->GetMethodID(clazz, "getNumObjects", "(III)I"); 1301 if (method_getNumObjects == NULL) { 1302 ALOGE("Can't find getNumObjects"); 1303 return -1; 1304 } 1305 method_getSupportedPlaybackFormats = env->GetMethodID(clazz, "getSupportedPlaybackFormats", "()[I"); 1306 if (method_getSupportedPlaybackFormats == NULL) { 1307 ALOGE("Can't find getSupportedPlaybackFormats"); 1308 return -1; 1309 } 1310 method_getSupportedCaptureFormats = env->GetMethodID(clazz, "getSupportedCaptureFormats", "()[I"); 1311 if (method_getSupportedCaptureFormats == NULL) { 1312 ALOGE("Can't find getSupportedCaptureFormats"); 1313 return -1; 1314 } 1315 method_getSupportedObjectProperties = env->GetMethodID(clazz, "getSupportedObjectProperties", "(I)[I"); 1316 if (method_getSupportedObjectProperties == NULL) { 1317 ALOGE("Can't find getSupportedObjectProperties"); 1318 return -1; 1319 } 1320 method_getSupportedDeviceProperties = env->GetMethodID(clazz, "getSupportedDeviceProperties", "()[I"); 1321 if (method_getSupportedDeviceProperties == NULL) { 1322 ALOGE("Can't find getSupportedDeviceProperties"); 1323 return -1; 1324 } 1325 method_setObjectProperty = env->GetMethodID(clazz, "setObjectProperty", "(IIJLjava/lang/String;)I"); 1326 if (method_setObjectProperty == NULL) { 1327 ALOGE("Can't find setObjectProperty"); 1328 return -1; 1329 } 1330 method_getDeviceProperty = env->GetMethodID(clazz, "getDeviceProperty", "(I[J[C)I"); 1331 if (method_getDeviceProperty == NULL) { 1332 ALOGE("Can't find getDeviceProperty"); 1333 return -1; 1334 } 1335 method_setDeviceProperty = env->GetMethodID(clazz, "setDeviceProperty", "(IJLjava/lang/String;)I"); 1336 if (method_setDeviceProperty == NULL) { 1337 ALOGE("Can't find setDeviceProperty"); 1338 return -1; 1339 } 1340 method_getObjectPropertyList = env->GetMethodID(clazz, "getObjectPropertyList", 1341 "(IIIII)Landroid/mtp/MtpPropertyList;"); 1342 if (method_getObjectPropertyList == NULL) { 1343 ALOGE("Can't find getObjectPropertyList"); 1344 return -1; 1345 } 1346 method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z"); 1347 if (method_getObjectInfo == NULL) { 1348 ALOGE("Can't find getObjectInfo"); 1349 return -1; 1350 } 1351 method_getObjectFilePath = env->GetMethodID(clazz, "getObjectFilePath", "(I[C[J)I"); 1352 if (method_getObjectFilePath == NULL) { 1353 ALOGE("Can't find getObjectFilePath"); 1354 return -1; 1355 } 1356 method_deleteFile = env->GetMethodID(clazz, "deleteFile", "(I)I"); 1357 if (method_deleteFile == NULL) { 1358 ALOGE("Can't find deleteFile"); 1359 return -1; 1360 } 1361 method_getObjectReferences = env->GetMethodID(clazz, "getObjectReferences", "(I)[I"); 1362 if (method_getObjectReferences == NULL) { 1363 ALOGE("Can't find getObjectReferences"); 1364 return -1; 1365 } 1366 method_setObjectReferences = env->GetMethodID(clazz, "setObjectReferences", "(I[I)I"); 1367 if (method_setObjectReferences == NULL) { 1368 ALOGE("Can't find setObjectReferences"); 1369 return -1; 1370 } 1371 method_sessionStarted = env->GetMethodID(clazz, "sessionStarted", "()V"); 1372 if (method_sessionStarted == NULL) { 1373 ALOGE("Can't find sessionStarted"); 1374 return -1; 1375 } 1376 method_sessionEnded = env->GetMethodID(clazz, "sessionEnded", "()V"); 1377 if (method_sessionEnded == NULL) { 1378 ALOGE("Can't find sessionEnded"); 1379 return -1; 1380 } 1381 1382 field_context = env->GetFieldID(clazz, "mNativeContext", "J"); 1383 if (field_context == NULL) { 1384 ALOGE("Can't find MtpDatabase.mNativeContext"); 1385 return -1; 1386 } 1387 field_batteryLevel = env->GetFieldID(clazz, "mBatteryLevel", "I"); 1388 if (field_batteryLevel == NULL) { 1389 ALOGE("Can't find MtpDatabase.mBatteryLevel"); 1390 return -1; 1391 } 1392 field_batteryScale = env->GetFieldID(clazz, "mBatteryScale", "I"); 1393 if (field_batteryScale == NULL) { 1394 ALOGE("Can't find MtpDatabase.mBatteryScale"); 1395 return -1; 1396 } 1397 field_deviceType = env->GetFieldID(clazz, "mDeviceType", "I"); 1398 if (field_deviceType == NULL) { 1399 ALOGE("Can't find MtpDatabase.mDeviceType"); 1400 return -1; 1401 } 1402 1403 // now set up fields for MtpPropertyList class 1404 clazz = env->FindClass("android/mtp/MtpPropertyList"); 1405 if (clazz == NULL) { 1406 ALOGE("Can't find android/mtp/MtpPropertyList"); 1407 return -1; 1408 } 1409 field_mCount = env->GetFieldID(clazz, "mCount", "I"); 1410 if (field_mCount == NULL) { 1411 ALOGE("Can't find MtpPropertyList.mCount"); 1412 return -1; 1413 } 1414 field_mResult = env->GetFieldID(clazz, "mResult", "I"); 1415 if (field_mResult == NULL) { 1416 ALOGE("Can't find MtpPropertyList.mResult"); 1417 return -1; 1418 } 1419 field_mObjectHandles = env->GetFieldID(clazz, "mObjectHandles", "[I"); 1420 if (field_mObjectHandles == NULL) { 1421 ALOGE("Can't find MtpPropertyList.mObjectHandles"); 1422 return -1; 1423 } 1424 field_mPropertyCodes = env->GetFieldID(clazz, "mPropertyCodes", "[I"); 1425 if (field_mPropertyCodes == NULL) { 1426 ALOGE("Can't find MtpPropertyList.mPropertyCodes"); 1427 return -1; 1428 } 1429 field_mDataTypes = env->GetFieldID(clazz, "mDataTypes", "[I"); 1430 if (field_mDataTypes == NULL) { 1431 ALOGE("Can't find MtpPropertyList.mDataTypes"); 1432 return -1; 1433 } 1434 field_mLongValues = env->GetFieldID(clazz, "mLongValues", "[J"); 1435 if (field_mLongValues == NULL) { 1436 ALOGE("Can't find MtpPropertyList.mLongValues"); 1437 return -1; 1438 } 1439 field_mStringValues = env->GetFieldID(clazz, "mStringValues", "[Ljava/lang/String;"); 1440 if (field_mStringValues == NULL) { 1441 ALOGE("Can't find MtpPropertyList.mStringValues"); 1442 return -1; 1443 } 1444 1445 if (AndroidRuntime::registerNativeMethods(env, 1446 "android/mtp/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods))) 1447 return -1; 1448 1449 return AndroidRuntime::registerNativeMethods(env, 1450 "android/mtp/MtpPropertyGroup", gMtpPropertyGroupMethods, NELEM(gMtpPropertyGroupMethods)); 1451} 1452