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