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