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