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