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