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