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