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