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