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_Utils.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             switch (type) {
    712                 case MTP_TYPE_INT8:
    713                     packet.putInt8(longValues[i]);
    714                     break;
    715                 case MTP_TYPE_UINT8:
    716                     packet.putUInt8(longValues[i]);
    717                     break;
    718                 case MTP_TYPE_INT16:
    719                     packet.putInt16(longValues[i]);
    720                     break;
    721                 case MTP_TYPE_UINT16:
    722                     packet.putUInt16(longValues[i]);
    723                     break;
    724                 case MTP_TYPE_INT32:
    725                     packet.putInt32(longValues[i]);
    726                     break;
    727                 case MTP_TYPE_UINT32:
    728                     packet.putUInt32(longValues[i]);
    729                     break;
    730                 case MTP_TYPE_INT64:
    731                     packet.putInt64(longValues[i]);
    732                     break;
    733                 case MTP_TYPE_UINT64:
    734                     packet.putUInt64(longValues[i]);
    735                     break;
    736                 case MTP_TYPE_INT128:
    737                     packet.putInt128(longValues[i]);
    738                     break;
    739                 case MTP_TYPE_UINT128:
    740                     packet.putUInt128(longValues[i]);
    741                     break;
    742                 case MTP_TYPE_STR: {
    743                     jstring value = (jstring)env->GetObjectArrayElement(stringValuesArray, i);
    744                     const char *valueStr = (value ? env->GetStringUTFChars(value, NULL) : NULL);
    745                     if (valueStr) {
    746                         packet.putString(valueStr);
    747                         env->ReleaseStringUTFChars(value, valueStr);
    748                     } else {
    749                         packet.putEmptyString();
    750                     }
    751                     env->DeleteLocalRef(value);
    752                     break;
    753                 }
    754                 default:
    755                     ALOGE("bad or unsupported data type in MtpDatabase::getObjectPropertyList");
    756                     break;
    757             }
    758         }
    759 
    760         env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0);
    761         env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0);
    762         env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0);
    763         env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
    764 
    765         env->DeleteLocalRef(objectHandlesArray);
    766         env->DeleteLocalRef(propertyCodesArray);
    767         env->DeleteLocalRef(dataTypesArray);
    768         env->DeleteLocalRef(longValuesArray);
    769         env->DeleteLocalRef(stringValuesArray);
    770     }
    771 
    772     env->DeleteLocalRef(list);
    773     checkAndClearExceptionFromCallback(env, __FUNCTION__);
    774     return result;
    775 }
    776 
    777 static void foreachentry(ExifEntry *entry, void* /* user */) {
    778     char buf[1024];
    779     ALOGI("entry %x, format %d, size %d: %s",
    780             entry->tag, entry->format, entry->size, exif_entry_get_value(entry, buf, sizeof(buf)));
    781 }
    782 
    783 static void foreachcontent(ExifContent *content, void *user) {
    784     ALOGI("content %d", exif_content_get_ifd(content));
    785     exif_content_foreach_entry(content, foreachentry, user);
    786 }
    787 
    788 static long getLongFromExifEntry(ExifEntry *e) {
    789     ExifByteOrder o = exif_data_get_byte_order(e->parent->parent);
    790     return exif_get_long(e->data, o);
    791 }
    792 
    793 static ExifData *getExifFromExtractor(const char *path) {
    794     std::unique_ptr<uint8_t[]> exifBuf;
    795     ExifData *exifdata = NULL;
    796 
    797     FILE *fp = fopen (path, "rb");
    798     if (!fp) {
    799         ALOGE("failed to open file");
    800         return NULL;
    801     }
    802 
    803     sp<NuMediaExtractor> extractor = new NuMediaExtractor();
    804     fseek(fp, 0L, SEEK_END);
    805     if (extractor->setDataSource(fileno(fp), 0, ftell(fp)) != OK) {
    806         ALOGE("failed to setDataSource");
    807         fclose(fp);
    808         return NULL;
    809     }
    810 
    811     off64_t offset;
    812     size_t size;
    813     if (extractor->getExifOffsetSize(&offset, &size) != OK) {
    814         fclose(fp);
    815         return NULL;
    816     }
    817 
    818     exifBuf.reset(new uint8_t[size]);
    819     fseek(fp, offset, SEEK_SET);
    820     if (fread(exifBuf.get(), 1, size, fp) == size) {
    821         exifdata = exif_data_new_from_data(exifBuf.get(), size);
    822     }
    823 
    824     fclose(fp);
    825     return exifdata;
    826 }
    827 
    828 MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
    829                                              MtpObjectInfo& info) {
    830     MtpStringBuffer path;
    831     int64_t         length;
    832     MtpObjectFormat format;
    833 
    834     MtpResponseCode result = getObjectFilePath(handle, path, length, format);
    835     if (result != MTP_RESPONSE_OK) {
    836         return result;
    837     }
    838     info.mCompressedSize = (length > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)length);
    839 
    840     JNIEnv* env = AndroidRuntime::getJNIEnv();
    841     if (!env->CallBooleanMethod(mDatabase, method_getObjectInfo,
    842                 (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer)) {
    843         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
    844     }
    845 
    846     jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
    847     info.mStorageID = intValues[0];
    848     info.mFormat = intValues[1];
    849     info.mParent = intValues[2];
    850     env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
    851 
    852     jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
    853     info.mDateCreated = longValues[0];
    854     info.mDateModified = longValues[1];
    855     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
    856 
    857     if ((false)) {
    858         info.mAssociationType = (format == MTP_FORMAT_ASSOCIATION ?
    859                                 MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
    860                                 MTP_ASSOCIATION_TYPE_UNDEFINED);
    861     }
    862     info.mAssociationType = MTP_ASSOCIATION_TYPE_UNDEFINED;
    863 
    864     jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
    865     MtpStringBuffer temp(str);
    866     info.mName = strdup(temp);
    867     env->ReleaseCharArrayElements(mStringBuffer, str, 0);
    868 
    869     // read EXIF data for thumbnail information
    870     switch (info.mFormat) {
    871         case MTP_FORMAT_EXIF_JPEG:
    872         case MTP_FORMAT_HEIF:
    873         case MTP_FORMAT_JFIF: {
    874             ExifData *exifdata;
    875             if (info.mFormat == MTP_FORMAT_HEIF) {
    876                 exifdata = getExifFromExtractor(path);
    877             } else {
    878                 exifdata = exif_data_new_from_file(path);
    879             }
    880             if (exifdata) {
    881                 if ((false)) {
    882                     exif_data_foreach_content(exifdata, foreachcontent, NULL);
    883                 }
    884 
    885                 ExifEntry *w = exif_content_get_entry(
    886                         exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_X_DIMENSION);
    887                 ExifEntry *h = exif_content_get_entry(
    888                         exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_Y_DIMENSION);
    889                 info.mThumbCompressedSize = exifdata->data ? exifdata->size : 0;
    890                 info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
    891                 info.mImagePixWidth = w ? getLongFromExifEntry(w) : 0;
    892                 info.mImagePixHeight = h ? getLongFromExifEntry(h) : 0;
    893                 exif_data_unref(exifdata);
    894             }
    895             break;
    896         }
    897 
    898         // Except DNG, all supported RAW image formats are not defined in PTP 1.2 specification.
    899         // Most of RAW image formats are based on TIFF or TIFF/EP. To render Fuji's RAF format,
    900         // it checks MTP_FORMAT_DEFINED case since it's designed as a custom format.
    901         case MTP_FORMAT_DNG:
    902         case MTP_FORMAT_TIFF:
    903         case MTP_FORMAT_TIFF_EP:
    904         case MTP_FORMAT_DEFINED: {
    905             String8 temp(path);
    906             std::unique_ptr<FileStream> stream(new FileStream(temp));
    907             piex::PreviewImageData image_data;
    908             if (!GetExifFromRawImage(stream.get(), temp, image_data)) {
    909                 // Couldn't parse EXIF data from a image file via piex.
    910                 break;
    911             }
    912 
    913             info.mThumbCompressedSize = image_data.thumbnail.length;
    914             info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
    915             info.mImagePixWidth = image_data.full_width;
    916             info.mImagePixHeight = image_data.full_height;
    917 
    918             break;
    919         }
    920     }
    921 
    922     checkAndClearExceptionFromCallback(env, __FUNCTION__);
    923     return MTP_RESPONSE_OK;
    924 }
    925 
    926 void* MtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
    927     MtpStringBuffer path;
    928     int64_t length;
    929     MtpObjectFormat format;
    930     void* result = NULL;
    931     outThumbSize = 0;
    932 
    933     if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK) {
    934         switch (format) {
    935             case MTP_FORMAT_EXIF_JPEG:
    936             case MTP_FORMAT_HEIF:
    937             case MTP_FORMAT_JFIF: {
    938                 ExifData *exifdata;
    939                 if (format == MTP_FORMAT_HEIF) {
    940                     exifdata = getExifFromExtractor(path);
    941                 } else {
    942                     exifdata = exif_data_new_from_file(path);
    943                 }
    944                 if (exifdata) {
    945                     if (exifdata->data) {
    946                         result = malloc(exifdata->size);
    947                         if (result) {
    948                             memcpy(result, exifdata->data, exifdata->size);
    949                             outThumbSize = exifdata->size;
    950                         }
    951                     }
    952                     exif_data_unref(exifdata);
    953                 }
    954                 break;
    955             }
    956 
    957             // See the above comment on getObjectInfo() method.
    958             case MTP_FORMAT_DNG:
    959             case MTP_FORMAT_TIFF:
    960             case MTP_FORMAT_TIFF_EP:
    961             case MTP_FORMAT_DEFINED: {
    962                 String8 temp(path);
    963                 std::unique_ptr<FileStream> stream(new FileStream(temp));
    964                 piex::PreviewImageData image_data;
    965                 if (!GetExifFromRawImage(stream.get(), temp, image_data)) {
    966                     // Couldn't parse EXIF data from a image file via piex.
    967                     break;
    968                 }
    969 
    970                 if (image_data.thumbnail.length == 0
    971                         || image_data.thumbnail.format != ::piex::Image::kJpegCompressed) {
    972                     // No thumbnail or non jpeg thumbnail.
    973                     break;
    974                 }
    975 
    976                 result = malloc(image_data.thumbnail.length);
    977                 if (result) {
    978                     piex::Error err = stream.get()->GetData(
    979                             image_data.thumbnail.offset,
    980                             image_data.thumbnail.length,
    981                             (std::uint8_t *)result);
    982                     if (err == piex::Error::kOk) {
    983                         outThumbSize = image_data.thumbnail.length;
    984                     } else {
    985                         free(result);
    986                         result = NULL;
    987                     }
    988                 }
    989                 break;
    990             }
    991         }
    992     }
    993 
    994     return result;
    995 }
    996 
    997 MtpResponseCode MtpDatabase::getObjectFilePath(MtpObjectHandle handle,
    998                                                  MtpStringBuffer& outFilePath,
    999                                                  int64_t& outFileLength,
   1000                                                  MtpObjectFormat& outFormat) {
   1001     JNIEnv* env = AndroidRuntime::getJNIEnv();
   1002     jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath,
   1003                 (jint)handle, mStringBuffer, mLongBuffer);
   1004     if (result != MTP_RESPONSE_OK) {
   1005         checkAndClearExceptionFromCallback(env, __FUNCTION__);
   1006         return result;
   1007     }
   1008 
   1009     jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
   1010     outFilePath.set(str);
   1011     env->ReleaseCharArrayElements(mStringBuffer, str, 0);
   1012 
   1013     jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
   1014     outFileLength = longValues[0];
   1015     outFormat = longValues[1];
   1016     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
   1017 
   1018     checkAndClearExceptionFromCallback(env, __FUNCTION__);
   1019     return result;
   1020 }
   1021 
   1022 MtpResponseCode MtpDatabase::beginDeleteObject(MtpObjectHandle handle) {
   1023     JNIEnv* env = AndroidRuntime::getJNIEnv();
   1024     MtpResponseCode result = env->CallIntMethod(mDatabase, method_beginDeleteObject, (jint)handle);
   1025 
   1026     checkAndClearExceptionFromCallback(env, __FUNCTION__);
   1027     return result;
   1028 }
   1029 
   1030 void MtpDatabase::endDeleteObject(MtpObjectHandle handle, bool succeeded) {
   1031     JNIEnv* env = AndroidRuntime::getJNIEnv();
   1032     env->CallVoidMethod(mDatabase, method_endDeleteObject, (jint)handle, (jboolean) succeeded);
   1033 
   1034     checkAndClearExceptionFromCallback(env, __FUNCTION__);
   1035 }
   1036 
   1037 MtpResponseCode MtpDatabase::beginMoveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
   1038         MtpStorageID newStorage) {
   1039     JNIEnv* env = AndroidRuntime::getJNIEnv();
   1040     MtpResponseCode result = env->CallIntMethod(mDatabase, method_beginMoveObject,
   1041                 (jint)handle, (jint)newParent, (jint) newStorage);
   1042 
   1043     checkAndClearExceptionFromCallback(env, __FUNCTION__);
   1044     return result;
   1045 }
   1046 
   1047 void MtpDatabase::endMoveObject(MtpObjectHandle oldParent, MtpObjectHandle newParent,
   1048                                             MtpStorageID oldStorage, MtpStorageID newStorage,
   1049                                              MtpObjectHandle handle, bool succeeded) {
   1050     JNIEnv* env = AndroidRuntime::getJNIEnv();
   1051     env->CallVoidMethod(mDatabase, method_endMoveObject,
   1052                 (jint)oldParent, (jint) newParent, (jint) oldStorage, (jint) newStorage,
   1053                 (jint) handle, (jboolean) succeeded);
   1054 
   1055     checkAndClearExceptionFromCallback(env, __FUNCTION__);
   1056 }
   1057 
   1058 MtpResponseCode MtpDatabase::beginCopyObject(MtpObjectHandle handle, MtpObjectHandle newParent,
   1059         MtpStorageID newStorage) {
   1060     JNIEnv* env = AndroidRuntime::getJNIEnv();
   1061     MtpResponseCode result = env->CallIntMethod(mDatabase, method_beginCopyObject,
   1062                 (jint)handle, (jint)newParent, (jint) newStorage);
   1063 
   1064     checkAndClearExceptionFromCallback(env, __FUNCTION__);
   1065     return result;
   1066 }
   1067 
   1068 void MtpDatabase::endCopyObject(MtpObjectHandle handle, bool succeeded) {
   1069     JNIEnv* env = AndroidRuntime::getJNIEnv();
   1070     env->CallVoidMethod(mDatabase, method_endCopyObject, (jint)handle, (jboolean)succeeded);
   1071 
   1072     checkAndClearExceptionFromCallback(env, __FUNCTION__);
   1073 }
   1074 
   1075 
   1076 struct PropertyTableEntry {
   1077     MtpObjectProperty   property;
   1078     int                 type;
   1079 };
   1080 
   1081 static const PropertyTableEntry   kObjectPropertyTable[] = {
   1082     {   MTP_PROPERTY_STORAGE_ID,        MTP_TYPE_UINT32     },
   1083     {   MTP_PROPERTY_OBJECT_FORMAT,     MTP_TYPE_UINT16     },
   1084     {   MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16     },
   1085     {   MTP_PROPERTY_OBJECT_SIZE,       MTP_TYPE_UINT64     },
   1086     {   MTP_PROPERTY_OBJECT_FILE_NAME,  MTP_TYPE_STR        },
   1087     {   MTP_PROPERTY_DATE_MODIFIED,     MTP_TYPE_STR        },
   1088     {   MTP_PROPERTY_PARENT_OBJECT,     MTP_TYPE_UINT32     },
   1089     {   MTP_PROPERTY_PERSISTENT_UID,    MTP_TYPE_UINT128    },
   1090     {   MTP_PROPERTY_NAME,              MTP_TYPE_STR        },
   1091     {   MTP_PROPERTY_DISPLAY_NAME,      MTP_TYPE_STR        },
   1092     {   MTP_PROPERTY_DATE_ADDED,        MTP_TYPE_STR        },
   1093     {   MTP_PROPERTY_ARTIST,            MTP_TYPE_STR        },
   1094     {   MTP_PROPERTY_ALBUM_NAME,        MTP_TYPE_STR        },
   1095     {   MTP_PROPERTY_ALBUM_ARTIST,      MTP_TYPE_STR        },
   1096     {   MTP_PROPERTY_TRACK,             MTP_TYPE_UINT16     },
   1097     {   MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_TYPE_STR    },
   1098     {   MTP_PROPERTY_GENRE,             MTP_TYPE_STR        },
   1099     {   MTP_PROPERTY_COMPOSER,          MTP_TYPE_STR        },
   1100     {   MTP_PROPERTY_DURATION,          MTP_TYPE_UINT32     },
   1101     {   MTP_PROPERTY_DESCRIPTION,       MTP_TYPE_STR        },
   1102     {   MTP_PROPERTY_AUDIO_WAVE_CODEC,  MTP_TYPE_UINT32     },
   1103     {   MTP_PROPERTY_BITRATE_TYPE,      MTP_TYPE_UINT16     },
   1104     {   MTP_PROPERTY_AUDIO_BITRATE,     MTP_TYPE_UINT32     },
   1105     {   MTP_PROPERTY_NUMBER_OF_CHANNELS,MTP_TYPE_UINT16     },
   1106     {   MTP_PROPERTY_SAMPLE_RATE,       MTP_TYPE_UINT32     },
   1107 };
   1108 
   1109 static const PropertyTableEntry   kDevicePropertyTable[] = {
   1110     {   MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,    MTP_TYPE_STR },
   1111     {   MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,       MTP_TYPE_STR },
   1112     {   MTP_DEVICE_PROPERTY_IMAGE_SIZE,                 MTP_TYPE_STR },
   1113     {   MTP_DEVICE_PROPERTY_BATTERY_LEVEL,              MTP_TYPE_UINT8 },
   1114     {   MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE,      MTP_TYPE_UINT32 },
   1115 };
   1116 
   1117 bool MtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
   1118     int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
   1119     const PropertyTableEntry* entry = kObjectPropertyTable;
   1120     for (int i = 0; i < count; i++, entry++) {
   1121         if (entry->property == property) {
   1122             type = entry->type;
   1123             return true;
   1124         }
   1125     }
   1126     return false;
   1127 }
   1128 
   1129 bool MtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
   1130     int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]);
   1131     const PropertyTableEntry* entry = kDevicePropertyTable;
   1132     for (int i = 0; i < count; i++, entry++) {
   1133         if (entry->property == property) {
   1134             type = entry->type;
   1135             return true;
   1136         }
   1137     }
   1138     return false;
   1139 }
   1140 
   1141 MtpObjectHandleList* MtpDatabase::getObjectReferences(MtpObjectHandle handle) {
   1142     JNIEnv* env = AndroidRuntime::getJNIEnv();
   1143     jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectReferences,
   1144                 (jint)handle);
   1145     if (!array)
   1146         return NULL;
   1147     MtpObjectHandleList* list = new MtpObjectHandleList();
   1148     jint* handles = env->GetIntArrayElements(array, 0);
   1149     jsize length = env->GetArrayLength(array);
   1150     for (int i = 0; i < length; i++)
   1151         list->push_back(handles[i]);
   1152     env->ReleaseIntArrayElements(array, handles, 0);
   1153     env->DeleteLocalRef(array);
   1154 
   1155     checkAndClearExceptionFromCallback(env, __FUNCTION__);
   1156     return list;
   1157 }
   1158 
   1159 MtpResponseCode MtpDatabase::setObjectReferences(MtpObjectHandle handle,
   1160                                                    MtpObjectHandleList* references) {
   1161     JNIEnv* env = AndroidRuntime::getJNIEnv();
   1162     int count = references->size();
   1163     jintArray array = env->NewIntArray(count);
   1164     if (!array) {
   1165         ALOGE("out of memory in setObjectReferences");
   1166         return false;
   1167     }
   1168     jint* handles = env->GetIntArrayElements(array, 0);
   1169      for (int i = 0; i < count; i++)
   1170         handles[i] = (*references)[i];
   1171     env->ReleaseIntArrayElements(array, handles, 0);
   1172     MtpResponseCode result = env->CallIntMethod(mDatabase, method_setObjectReferences,
   1173                 (jint)handle, array);
   1174     env->DeleteLocalRef(array);
   1175 
   1176     checkAndClearExceptionFromCallback(env, __FUNCTION__);
   1177     return result;
   1178 }
   1179 
   1180 MtpProperty* MtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
   1181                                                   MtpObjectFormat format) {
   1182     static const int channelEnum[] = {
   1183                                         1,  // mono
   1184                                         2,  // stereo
   1185                                         3,  // 2.1
   1186                                         4,  // 3
   1187                                         5,  // 3.1
   1188                                         6,  // 4
   1189                                         7,  // 4.1
   1190                                         8,  // 5
   1191                                         9,  // 5.1
   1192                                     };
   1193     static const int bitrateEnum[] = {
   1194                                         1,  // fixed rate
   1195                                         2,  // variable rate
   1196                                      };
   1197 
   1198     MtpProperty* result = NULL;
   1199     switch (property) {
   1200         case MTP_PROPERTY_OBJECT_FORMAT:
   1201             // use format as default value
   1202             result = new MtpProperty(property, MTP_TYPE_UINT16, false, format);
   1203             break;
   1204         case MTP_PROPERTY_PROTECTION_STATUS:
   1205         case MTP_PROPERTY_TRACK:
   1206             result = new MtpProperty(property, MTP_TYPE_UINT16);
   1207             break;
   1208         case MTP_PROPERTY_STORAGE_ID:
   1209         case MTP_PROPERTY_PARENT_OBJECT:
   1210         case MTP_PROPERTY_DURATION:
   1211         case MTP_PROPERTY_AUDIO_WAVE_CODEC:
   1212             result = new MtpProperty(property, MTP_TYPE_UINT32);
   1213             break;
   1214         case MTP_PROPERTY_OBJECT_SIZE:
   1215             result = new MtpProperty(property, MTP_TYPE_UINT64);
   1216             break;
   1217         case MTP_PROPERTY_PERSISTENT_UID:
   1218             result = new MtpProperty(property, MTP_TYPE_UINT128);
   1219             break;
   1220         case MTP_PROPERTY_NAME:
   1221         case MTP_PROPERTY_DISPLAY_NAME:
   1222         case MTP_PROPERTY_ARTIST:
   1223         case MTP_PROPERTY_ALBUM_NAME:
   1224         case MTP_PROPERTY_ALBUM_ARTIST:
   1225         case MTP_PROPERTY_GENRE:
   1226         case MTP_PROPERTY_COMPOSER:
   1227         case MTP_PROPERTY_DESCRIPTION:
   1228             result = new MtpProperty(property, MTP_TYPE_STR);
   1229             break;
   1230         case MTP_PROPERTY_DATE_MODIFIED:
   1231         case MTP_PROPERTY_DATE_ADDED:
   1232         case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
   1233             result = new MtpProperty(property, MTP_TYPE_STR);
   1234             result->setFormDateTime();
   1235             break;
   1236         case MTP_PROPERTY_OBJECT_FILE_NAME:
   1237             // We allow renaming files and folders
   1238             result = new MtpProperty(property, MTP_TYPE_STR, true);
   1239             break;
   1240         case MTP_PROPERTY_BITRATE_TYPE:
   1241              result = new MtpProperty(property, MTP_TYPE_UINT16);
   1242             result->setFormEnum(bitrateEnum, sizeof(bitrateEnum)/sizeof(bitrateEnum[0]));
   1243             break;
   1244         case MTP_PROPERTY_AUDIO_BITRATE:
   1245             result = new MtpProperty(property, MTP_TYPE_UINT32);
   1246             result->setFormRange(1, 1536000, 1);
   1247             break;
   1248         case MTP_PROPERTY_NUMBER_OF_CHANNELS:
   1249             result = new MtpProperty(property, MTP_TYPE_UINT16);
   1250             result->setFormEnum(channelEnum, sizeof(channelEnum)/sizeof(channelEnum[0]));
   1251             break;
   1252         case MTP_PROPERTY_SAMPLE_RATE:
   1253             result = new MtpProperty(property, MTP_TYPE_UINT32);
   1254             result->setFormRange(8000, 48000, 1);
   1255             break;
   1256     }
   1257 
   1258     return result;
   1259 }
   1260 
   1261 MtpProperty* MtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
   1262     JNIEnv* env = AndroidRuntime::getJNIEnv();
   1263     MtpProperty* result = NULL;
   1264     bool writable = false;
   1265 
   1266     // get current value
   1267     jint ret = env->CallIntMethod(mDatabase, method_getDeviceProperty,
   1268         (jint)property, mLongBuffer, mStringBuffer);
   1269     if (ret == MTP_RESPONSE_OK) {
   1270         switch (property) {
   1271             case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
   1272             case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
   1273                 writable = true;
   1274                 // fall through
   1275             case MTP_DEVICE_PROPERTY_IMAGE_SIZE:
   1276             {
   1277                 result = new MtpProperty(property, MTP_TYPE_STR, writable);
   1278                 jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
   1279                 result->setCurrentValue(str);
   1280                 // for read-only properties it is safe to assume current value is default value
   1281                 if (!writable)
   1282                     result->setDefaultValue(str);
   1283                 env->ReleaseCharArrayElements(mStringBuffer, str, 0);
   1284                 break;
   1285             }
   1286             case MTP_DEVICE_PROPERTY_BATTERY_LEVEL:
   1287             {
   1288                 result = new MtpProperty(property, MTP_TYPE_UINT8);
   1289                 jlong* arr = env->GetLongArrayElements(mLongBuffer, 0);
   1290                 result->setFormRange(0, arr[1], 1);
   1291                 result->mCurrentValue.u.u8 = (uint8_t) arr[0];
   1292                 env->ReleaseLongArrayElements(mLongBuffer, arr, 0);
   1293                 break;
   1294             }
   1295             case MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE:
   1296             {
   1297                 jlong* arr = env->GetLongArrayElements(mLongBuffer, 0);
   1298                 result = new MtpProperty(property, MTP_TYPE_UINT32);
   1299                 result->mCurrentValue.u.u32 = (uint32_t) arr[0];
   1300                 env->ReleaseLongArrayElements(mLongBuffer, arr, 0);
   1301                 break;
   1302             }
   1303             default:
   1304                 ALOGE("Unrecognized property %x", property);
   1305         }
   1306     } else {
   1307         ALOGE("unable to read device property, response: %04X", ret);
   1308     }
   1309 
   1310     checkAndClearExceptionFromCallback(env, __FUNCTION__);
   1311     return result;
   1312 }
   1313 
   1314 // ----------------------------------------------------------------------------
   1315 
   1316 static void
   1317 android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz)
   1318 {
   1319     MtpDatabase* database = new MtpDatabase(env, thiz);
   1320     env->SetLongField(thiz, field_context, (jlong)database);
   1321     checkAndClearExceptionFromCallback(env, __FUNCTION__);
   1322 }
   1323 
   1324 static void
   1325 android_mtp_MtpDatabase_finalize(JNIEnv *env, jobject thiz)
   1326 {
   1327     MtpDatabase* database = (MtpDatabase *)env->GetLongField(thiz, field_context);
   1328     database->cleanup(env);
   1329     delete database;
   1330     env->SetLongField(thiz, field_context, 0);
   1331     checkAndClearExceptionFromCallback(env, __FUNCTION__);
   1332 }
   1333 
   1334 static jstring
   1335 android_mtp_MtpPropertyGroup_format_date_time(JNIEnv *env, jobject /*thiz*/, jlong seconds)
   1336 {
   1337     char    date[20];
   1338     formatDateTime(seconds, date, sizeof(date));
   1339     return env->NewStringUTF(date);
   1340 }
   1341 
   1342 // ----------------------------------------------------------------------------
   1343 
   1344 static const JNINativeMethod gMtpDatabaseMethods[] = {
   1345     {"native_setup",            "()V",  (void *)android_mtp_MtpDatabase_setup},
   1346     {"native_finalize",         "()V",  (void *)android_mtp_MtpDatabase_finalize},
   1347 };
   1348 
   1349 static const JNINativeMethod gMtpPropertyGroupMethods[] = {
   1350     {"format_date_time",        "(J)Ljava/lang/String;",
   1351                                         (void *)android_mtp_MtpPropertyGroup_format_date_time},
   1352 };
   1353 
   1354 #define GET_METHOD_ID(name, jclass, signature)                              \
   1355     method_##name = env->GetMethodID(jclass, #name, signature);             \
   1356     if (method_##name == NULL) {                                            \
   1357         ALOGE("Can't find " #name);                                         \
   1358         return -1;                                                          \
   1359     }                                                                       \
   1360 
   1361 int register_android_mtp_MtpDatabase(JNIEnv *env)
   1362 {
   1363     jclass clazz;
   1364 
   1365     clazz = env->FindClass("android/mtp/MtpDatabase");
   1366     if (clazz == NULL) {
   1367         ALOGE("Can't find android/mtp/MtpDatabase");
   1368         return -1;
   1369     }
   1370     GET_METHOD_ID(beginSendObject, clazz, "(Ljava/lang/String;III)I");
   1371     GET_METHOD_ID(endSendObject, clazz, "(IZ)V");
   1372     GET_METHOD_ID(rescanFile, clazz, "(Ljava/lang/String;II)V");
   1373     GET_METHOD_ID(getObjectList, clazz, "(III)[I");
   1374     GET_METHOD_ID(getNumObjects, clazz, "(III)I");
   1375     GET_METHOD_ID(getSupportedPlaybackFormats, clazz, "()[I");
   1376     GET_METHOD_ID(getSupportedCaptureFormats, clazz, "()[I");
   1377     GET_METHOD_ID(getSupportedObjectProperties, clazz, "(I)[I");
   1378     GET_METHOD_ID(getSupportedDeviceProperties, clazz, "()[I");
   1379     GET_METHOD_ID(setObjectProperty, clazz, "(IIJLjava/lang/String;)I");
   1380     GET_METHOD_ID(getDeviceProperty, clazz, "(I[J[C)I");
   1381     GET_METHOD_ID(setDeviceProperty, clazz, "(IJLjava/lang/String;)I");
   1382     GET_METHOD_ID(getObjectPropertyList, clazz, "(IIIII)Landroid/mtp/MtpPropertyList;");
   1383     GET_METHOD_ID(getObjectInfo, clazz, "(I[I[C[J)Z");
   1384     GET_METHOD_ID(getObjectFilePath, clazz, "(I[C[J)I");
   1385     GET_METHOD_ID(beginDeleteObject, clazz, "(I)I");
   1386     GET_METHOD_ID(endDeleteObject, clazz, "(IZ)V");
   1387     GET_METHOD_ID(beginMoveObject, clazz, "(III)I");
   1388     GET_METHOD_ID(endMoveObject, clazz, "(IIIIIZ)V");
   1389     GET_METHOD_ID(beginCopyObject, clazz, "(III)I");
   1390     GET_METHOD_ID(endCopyObject, clazz, "(IZ)V");
   1391     GET_METHOD_ID(getObjectReferences, clazz, "(I)[I");
   1392     GET_METHOD_ID(setObjectReferences, clazz, "(I[I)I");
   1393 
   1394     field_context = env->GetFieldID(clazz, "mNativeContext", "J");
   1395     if (field_context == NULL) {
   1396         ALOGE("Can't find MtpDatabase.mNativeContext");
   1397         return -1;
   1398     }
   1399 
   1400     clazz = env->FindClass("android/mtp/MtpPropertyList");
   1401     if (clazz == NULL) {
   1402         ALOGE("Can't find android/mtp/MtpPropertyList");
   1403         return -1;
   1404     }
   1405     GET_METHOD_ID(getCode, clazz, "()I");
   1406     GET_METHOD_ID(getCount, clazz, "()I");
   1407     GET_METHOD_ID(getObjectHandles, clazz, "()[I");
   1408     GET_METHOD_ID(getPropertyCodes, clazz, "()[I");
   1409     GET_METHOD_ID(getDataTypes, clazz, "()[I");
   1410     GET_METHOD_ID(getLongValues, clazz, "()[J");
   1411     GET_METHOD_ID(getStringValues, clazz, "()[Ljava/lang/String;");
   1412 
   1413     if (AndroidRuntime::registerNativeMethods(env,
   1414                 "android/mtp/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods)))
   1415         return -1;
   1416 
   1417     return AndroidRuntime::registerNativeMethods(env,
   1418                 "android/mtp/MtpPropertyGroup", gMtpPropertyGroupMethods, NELEM(gMtpPropertyGroupMethods));
   1419 }
   1420