android_mtp_MtpDatabase.cpp revision a792c804025e25eb8a110d1db63f26bbd09e0e6b
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 <stdio.h>
21#include <assert.h>
22#include <limits.h>
23#include <unistd.h>
24#include <fcntl.h>
25
26#include "jni.h"
27#include "JNIHelp.h"
28#include "android_runtime/AndroidRuntime.h"
29
30#include "MtpDatabase.h"
31#include "MtpDataPacket.h"
32#include "MtpObjectInfo.h"
33#include "MtpProperty.h"
34#include "MtpStringBuffer.h"
35#include "MtpUtils.h"
36#include "mtp.h"
37
38extern "C" {
39#include "jhead.h"
40}
41
42using namespace android;
43
44// ----------------------------------------------------------------------------
45
46static jmethodID method_beginSendObject;
47static jmethodID method_endSendObject;
48static jmethodID method_getObjectList;
49static jmethodID method_getNumObjects;
50static jmethodID method_getSupportedPlaybackFormats;
51static jmethodID method_getSupportedCaptureFormats;
52static jmethodID method_getSupportedObjectProperties;
53static jmethodID method_getSupportedDeviceProperties;
54static jmethodID method_setObjectProperty;
55static jmethodID method_getDeviceProperty;
56static jmethodID method_setDeviceProperty;
57static jmethodID method_getObjectPropertyList;
58static jmethodID method_getObjectInfo;
59static jmethodID method_getObjectFilePath;
60static jmethodID method_deleteFile;
61static jmethodID method_getObjectReferences;
62static jmethodID method_setObjectReferences;
63static jmethodID method_sessionStarted;
64static jmethodID method_sessionEnded;
65
66static jfieldID field_context;
67
68// MtpPropertyList fields
69static jfieldID field_mCount;
70static jfieldID field_mResult;
71static jfieldID field_mObjectHandles;
72static jfieldID field_mPropertyCodes;
73static jfieldID field_mDataTypes;
74static jfieldID field_mLongValues;
75static jfieldID field_mStringValues;
76
77
78MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
79    return (MtpDatabase *)env->GetIntField(database, field_context);
80}
81
82#ifdef HAVE_ANDROID_OS
83// ----------------------------------------------------------------------------
84
85class MyMtpDatabase : public MtpDatabase {
86private:
87    jobject         mDatabase;
88    jintArray       mIntBuffer;
89    jlongArray      mLongBuffer;
90    jcharArray      mStringBuffer;
91
92public:
93                                    MyMtpDatabase(JNIEnv *env, jobject client);
94    virtual                         ~MyMtpDatabase();
95    void                            cleanup(JNIEnv *env);
96
97    virtual MtpObjectHandle         beginSendObject(const char* path,
98                                            MtpObjectFormat format,
99                                            MtpObjectHandle parent,
100                                            MtpStorageID storage,
101                                            uint64_t size,
102                                            time_t modified);
103
104    virtual void                    endSendObject(const char* path,
105                                            MtpObjectHandle handle,
106                                            MtpObjectFormat format,
107                                            bool succeeded);
108
109    virtual MtpObjectHandleList*    getObjectList(MtpStorageID storageID,
110                                    MtpObjectFormat format,
111                                    MtpObjectHandle parent);
112
113    virtual int                     getNumObjects(MtpStorageID storageID,
114                                            MtpObjectFormat format,
115                                            MtpObjectHandle parent);
116
117    // callee should delete[] the results from these
118    // results can be NULL
119    virtual MtpObjectFormatList*    getSupportedPlaybackFormats();
120    virtual MtpObjectFormatList*    getSupportedCaptureFormats();
121    virtual MtpObjectPropertyList*  getSupportedObjectProperties(MtpObjectFormat format);
122    virtual MtpDevicePropertyList*  getSupportedDeviceProperties();
123
124    virtual MtpResponseCode         getObjectPropertyValue(MtpObjectHandle handle,
125                                            MtpObjectProperty property,
126                                            MtpDataPacket& packet);
127
128    virtual MtpResponseCode         setObjectPropertyValue(MtpObjectHandle handle,
129                                            MtpObjectProperty property,
130                                            MtpDataPacket& packet);
131
132    virtual MtpResponseCode         getDevicePropertyValue(MtpDeviceProperty property,
133                                            MtpDataPacket& packet);
134
135    virtual MtpResponseCode         setDevicePropertyValue(MtpDeviceProperty property,
136                                            MtpDataPacket& packet);
137
138    virtual MtpResponseCode         resetDeviceProperty(MtpDeviceProperty property);
139
140    virtual MtpResponseCode         getObjectPropertyList(MtpObjectHandle handle,
141                                            uint32_t format, uint32_t property,
142                                            int groupCode, int depth,
143                                            MtpDataPacket& packet);
144
145    virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
146                                            MtpObjectInfo& info);
147
148    virtual void*                   getThumbnail(MtpObjectHandle handle, size_t& outThumbSize);
149
150    virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
151                                            MtpString& outFilePath,
152                                            int64_t& outFileLength,
153                                            MtpObjectFormat& outFormat);
154    virtual MtpResponseCode         deleteFile(MtpObjectHandle handle);
155
156    bool                            getObjectPropertyInfo(MtpObjectProperty property, int& type);
157    bool                            getDevicePropertyInfo(MtpDeviceProperty property, int& type);
158
159    virtual MtpObjectHandleList*    getObjectReferences(MtpObjectHandle handle);
160
161    virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
162                                            MtpObjectHandleList* references);
163
164    virtual MtpProperty*            getObjectPropertyDesc(MtpObjectProperty property,
165                                            MtpObjectFormat format);
166
167    virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property);
168
169    virtual void                    sessionStarted();
170
171    virtual void                    sessionEnded();
172};
173
174// ----------------------------------------------------------------------------
175
176static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
177    if (env->ExceptionCheck()) {
178        LOGE("An exception was thrown by callback '%s'.", methodName);
179        LOGE_EX(env);
180        env->ExceptionClear();
181    }
182}
183
184// ----------------------------------------------------------------------------
185
186MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client)
187    :   mDatabase(env->NewGlobalRef(client)),
188        mIntBuffer(NULL),
189        mLongBuffer(NULL),
190        mStringBuffer(NULL)
191{
192    jintArray intArray;
193    jlongArray longArray;
194    jcharArray charArray;
195
196    // create buffers for out arguments
197    // we don't need to be thread-safe so this is OK
198    intArray = env->NewIntArray(3);
199    if (!intArray)
200        goto out_of_memory;
201    mIntBuffer = (jintArray)env->NewGlobalRef(intArray);
202    longArray = env->NewLongArray(2);
203    if (!longArray)
204        goto out_of_memory;
205    mLongBuffer = (jlongArray)env->NewGlobalRef(longArray);
206    charArray = env->NewCharArray(256);
207    if (!charArray)
208        goto out_of_memory;
209    mStringBuffer = (jcharArray)env->NewGlobalRef(charArray);
210    return;
211
212out_of_memory:
213    env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), NULL);
214}
215
216void MyMtpDatabase::cleanup(JNIEnv *env) {
217    env->DeleteGlobalRef(mDatabase);
218    env->DeleteGlobalRef(mIntBuffer);
219    env->DeleteGlobalRef(mLongBuffer);
220    env->DeleteGlobalRef(mStringBuffer);
221}
222
223MyMtpDatabase::~MyMtpDatabase() {
224}
225
226MtpObjectHandle MyMtpDatabase::beginSendObject(const char* path,
227                                            MtpObjectFormat format,
228                                            MtpObjectHandle parent,
229                                            MtpStorageID storage,
230                                            uint64_t size,
231                                            time_t modified) {
232    JNIEnv* env = AndroidRuntime::getJNIEnv();
233    jstring pathStr = env->NewStringUTF(path);
234    MtpObjectHandle result = env->CallIntMethod(mDatabase, method_beginSendObject,
235            pathStr, (jint)format, (jint)parent, (jint)storage,
236            (jlong)size, (jlong)modified);
237
238    if (pathStr)
239        env->DeleteLocalRef(pathStr);
240    checkAndClearExceptionFromCallback(env, __FUNCTION__);
241    return result;
242}
243
244void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle,
245                                MtpObjectFormat format, bool succeeded) {
246    JNIEnv* env = AndroidRuntime::getJNIEnv();
247    jstring pathStr = env->NewStringUTF(path);
248    env->CallVoidMethod(mDatabase, method_endSendObject, pathStr,
249                        (jint)handle, (jint)format, (jboolean)succeeded);
250
251    if (pathStr)
252        env->DeleteLocalRef(pathStr);
253    checkAndClearExceptionFromCallback(env, __FUNCTION__);
254}
255
256MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID,
257                                    MtpObjectFormat format,
258                                    MtpObjectHandle parent) {
259    JNIEnv* env = AndroidRuntime::getJNIEnv();
260    jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectList,
261                (jint)storageID, (jint)format, (jint)parent);
262    if (!array)
263        return NULL;
264    MtpObjectHandleList* list = new MtpObjectHandleList();
265    jint* handles = env->GetIntArrayElements(array, 0);
266    jsize length = env->GetArrayLength(array);
267    for (int i = 0; i < length; i++)
268        list->push(handles[i]);
269    env->ReleaseIntArrayElements(array, handles, 0);
270    env->DeleteLocalRef(array);
271
272    checkAndClearExceptionFromCallback(env, __FUNCTION__);
273    return list;
274}
275
276int MyMtpDatabase::getNumObjects(MtpStorageID storageID,
277                                MtpObjectFormat format,
278                                MtpObjectHandle parent) {
279    JNIEnv* env = AndroidRuntime::getJNIEnv();
280    int result = env->CallIntMethod(mDatabase, method_getNumObjects,
281                (jint)storageID, (jint)format, (jint)parent);
282
283    checkAndClearExceptionFromCallback(env, __FUNCTION__);
284    return result;
285}
286
287MtpObjectFormatList* MyMtpDatabase::getSupportedPlaybackFormats() {
288    JNIEnv* env = AndroidRuntime::getJNIEnv();
289    jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
290            method_getSupportedPlaybackFormats);
291    if (!array)
292        return NULL;
293    MtpObjectFormatList* list = new MtpObjectFormatList();
294    jint* formats = env->GetIntArrayElements(array, 0);
295    jsize length = env->GetArrayLength(array);
296    for (int i = 0; i < length; i++)
297        list->push(formats[i]);
298    env->ReleaseIntArrayElements(array, formats, 0);
299    env->DeleteLocalRef(array);
300
301    checkAndClearExceptionFromCallback(env, __FUNCTION__);
302    return list;
303}
304
305MtpObjectFormatList* MyMtpDatabase::getSupportedCaptureFormats() {
306    JNIEnv* env = AndroidRuntime::getJNIEnv();
307    jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
308            method_getSupportedCaptureFormats);
309    if (!array)
310        return NULL;
311    MtpObjectFormatList* list = new MtpObjectFormatList();
312    jint* formats = env->GetIntArrayElements(array, 0);
313    jsize length = env->GetArrayLength(array);
314    for (int i = 0; i < length; i++)
315        list->push(formats[i]);
316    env->ReleaseIntArrayElements(array, formats, 0);
317    env->DeleteLocalRef(array);
318
319    checkAndClearExceptionFromCallback(env, __FUNCTION__);
320    return list;
321}
322
323MtpObjectPropertyList* MyMtpDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
324    JNIEnv* env = AndroidRuntime::getJNIEnv();
325    jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
326            method_getSupportedObjectProperties, (jint)format);
327    if (!array)
328        return NULL;
329    MtpObjectPropertyList* list = new MtpObjectPropertyList();
330    jint* properties = env->GetIntArrayElements(array, 0);
331    jsize length = env->GetArrayLength(array);
332    for (int i = 0; i < length; i++)
333        list->push(properties[i]);
334    env->ReleaseIntArrayElements(array, properties, 0);
335    env->DeleteLocalRef(array);
336
337    checkAndClearExceptionFromCallback(env, __FUNCTION__);
338    return list;
339}
340
341MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() {
342    JNIEnv* env = AndroidRuntime::getJNIEnv();
343    jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
344            method_getSupportedDeviceProperties);
345    if (!array)
346        return NULL;
347    MtpDevicePropertyList* list = new MtpDevicePropertyList();
348    jint* properties = env->GetIntArrayElements(array, 0);
349    jsize length = env->GetArrayLength(array);
350    for (int i = 0; i < length; i++)
351        list->push(properties[i]);
352    env->ReleaseIntArrayElements(array, properties, 0);
353    env->DeleteLocalRef(array);
354
355    checkAndClearExceptionFromCallback(env, __FUNCTION__);
356    return list;
357}
358
359MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
360                                            MtpObjectProperty property,
361                                            MtpDataPacket& packet) {
362    JNIEnv* env = AndroidRuntime::getJNIEnv();
363    jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList,
364                (jlong)handle, 0, (jlong)property, 0, 0);
365    MtpResponseCode result = env->GetIntField(list, field_mResult);
366    int count = env->GetIntField(list, field_mCount);
367    if (result == MTP_RESPONSE_OK && count != 1)
368        result = MTP_RESPONSE_GENERAL_ERROR;
369
370    if (result == MTP_RESPONSE_OK) {
371        jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles);
372        jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes);
373        jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes);
374        jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues);
375        jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues);
376
377        jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0);
378        jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0);
379        jint* dataTypes = env->GetIntArrayElements(dataTypesArray, 0);
380        jlong* longValues = (longValuesArray ? env->GetLongArrayElements(longValuesArray, 0) : NULL);
381
382        int type = dataTypes[0];
383        jlong longValue = (longValues ? longValues[0] : 0);
384
385        // special case date properties, which are strings to MTP
386        // but stored internally as a uint64
387        if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) {
388            char    date[20];
389            formatDateTime(longValue, date, sizeof(date));
390            packet.putString(date);
391            goto out;
392        }
393        // release date is stored internally as just the year
394        if (property == MTP_PROPERTY_ORIGINAL_RELEASE_DATE) {
395            char    date[20];
396            snprintf(date, sizeof(date), "%04lld0101T000000", longValue);
397            packet.putString(date);
398            goto out;
399        }
400
401        switch (type) {
402            case MTP_TYPE_INT8:
403                packet.putInt8(longValue);
404                break;
405            case MTP_TYPE_UINT8:
406                packet.putUInt8(longValue);
407                break;
408            case MTP_TYPE_INT16:
409                packet.putInt16(longValue);
410                break;
411            case MTP_TYPE_UINT16:
412                packet.putUInt16(longValue);
413                break;
414            case MTP_TYPE_INT32:
415                packet.putInt32(longValue);
416                break;
417            case MTP_TYPE_UINT32:
418                packet.putUInt32(longValue);
419                break;
420            case MTP_TYPE_INT64:
421                packet.putInt64(longValue);
422                break;
423            case MTP_TYPE_UINT64:
424                packet.putUInt64(longValue);
425                break;
426            case MTP_TYPE_INT128:
427                packet.putInt128(longValue);
428                break;
429            case MTP_TYPE_UINT128:
430                packet.putInt128(longValue);
431                break;
432            case MTP_TYPE_STR:
433            {
434                jstring stringValue = (jstring)env->GetObjectArrayElement(stringValuesArray, 0);
435                if (stringValue) {
436                    const char* str = env->GetStringUTFChars(stringValue, NULL);
437                    packet.putString(str);
438                    env->ReleaseStringUTFChars(stringValue, str);
439                } else {
440                    packet.putEmptyString();
441                }
442                break;
443             }
444            default:
445                LOGE("unsupported type in getObjectPropertyValue\n");
446                result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
447        }
448out:
449        env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0);
450        env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0);
451        env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0);
452        if (longValues)
453            env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
454
455        env->DeleteLocalRef(objectHandlesArray);
456        env->DeleteLocalRef(propertyCodesArray);
457        env->DeleteLocalRef(dataTypesArray);
458        if (longValuesArray)
459            env->DeleteLocalRef(longValuesArray);
460        if (stringValuesArray)
461            env->DeleteLocalRef(stringValuesArray);
462    }
463
464    env->DeleteLocalRef(list);
465    checkAndClearExceptionFromCallback(env, __FUNCTION__);
466    return result;
467}
468
469MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
470                                            MtpObjectProperty property,
471                                            MtpDataPacket& packet) {
472    int         type;
473
474    if (!getObjectPropertyInfo(property, type))
475        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
476
477    JNIEnv* env = AndroidRuntime::getJNIEnv();
478    jlong longValue = 0;
479    jstring stringValue = NULL;
480
481    switch (type) {
482        case MTP_TYPE_INT8:
483            longValue = packet.getInt8();
484            break;
485        case MTP_TYPE_UINT8:
486            longValue = packet.getUInt8();
487            break;
488        case MTP_TYPE_INT16:
489            longValue = packet.getInt16();
490            break;
491        case MTP_TYPE_UINT16:
492            longValue = packet.getUInt16();
493            break;
494        case MTP_TYPE_INT32:
495            longValue = packet.getInt32();
496            break;
497        case MTP_TYPE_UINT32:
498            longValue = packet.getUInt32();
499            break;
500        case MTP_TYPE_INT64:
501            longValue = packet.getInt64();
502            break;
503        case MTP_TYPE_UINT64:
504            longValue = packet.getUInt64();
505            break;
506        case MTP_TYPE_STR:
507        {
508            MtpStringBuffer buffer;
509            packet.getString(buffer);
510            stringValue = env->NewStringUTF((const char *)buffer);
511            break;
512         }
513        default:
514            LOGE("unsupported type in getObjectPropertyValue\n");
515            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
516    }
517
518    jint result = env->CallIntMethod(mDatabase, method_setObjectProperty,
519                (jint)handle, (jint)property, longValue, stringValue);
520    if (stringValue)
521        env->DeleteLocalRef(stringValue);
522
523    checkAndClearExceptionFromCallback(env, __FUNCTION__);
524    return result;
525}
526
527MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
528                                            MtpDataPacket& packet) {
529    int         type;
530
531    if (!getDevicePropertyInfo(property, type))
532        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
533
534    JNIEnv* env = AndroidRuntime::getJNIEnv();
535    jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty,
536                (jint)property, mLongBuffer, mStringBuffer);
537    if (result != MTP_RESPONSE_OK) {
538        checkAndClearExceptionFromCallback(env, __FUNCTION__);
539        return result;
540    }
541
542    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
543    jlong longValue = longValues[0];
544    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
545
546    switch (type) {
547        case MTP_TYPE_INT8:
548            packet.putInt8(longValue);
549            break;
550        case MTP_TYPE_UINT8:
551            packet.putUInt8(longValue);
552            break;
553        case MTP_TYPE_INT16:
554            packet.putInt16(longValue);
555            break;
556        case MTP_TYPE_UINT16:
557            packet.putUInt16(longValue);
558            break;
559        case MTP_TYPE_INT32:
560            packet.putInt32(longValue);
561            break;
562        case MTP_TYPE_UINT32:
563            packet.putUInt32(longValue);
564            break;
565        case MTP_TYPE_INT64:
566            packet.putInt64(longValue);
567            break;
568        case MTP_TYPE_UINT64:
569            packet.putUInt64(longValue);
570            break;
571        case MTP_TYPE_INT128:
572            packet.putInt128(longValue);
573            break;
574        case MTP_TYPE_UINT128:
575            packet.putInt128(longValue);
576            break;
577        case MTP_TYPE_STR:
578        {
579            jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
580            packet.putString(str);
581            env->ReleaseCharArrayElements(mStringBuffer, str, 0);
582            break;
583         }
584        default:
585            LOGE("unsupported type in getDevicePropertyValue\n");
586            return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
587    }
588
589    checkAndClearExceptionFromCallback(env, __FUNCTION__);
590    return MTP_RESPONSE_OK;
591}
592
593MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property,
594                                            MtpDataPacket& packet) {
595    int         type;
596
597    if (!getDevicePropertyInfo(property, type))
598        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
599
600    JNIEnv* env = AndroidRuntime::getJNIEnv();
601    jlong longValue = 0;
602    jstring stringValue = NULL;
603
604    switch (type) {
605        case MTP_TYPE_INT8:
606            longValue = packet.getInt8();
607            break;
608        case MTP_TYPE_UINT8:
609            longValue = packet.getUInt8();
610            break;
611        case MTP_TYPE_INT16:
612            longValue = packet.getInt16();
613            break;
614        case MTP_TYPE_UINT16:
615            longValue = packet.getUInt16();
616            break;
617        case MTP_TYPE_INT32:
618            longValue = packet.getInt32();
619            break;
620        case MTP_TYPE_UINT32:
621            longValue = packet.getUInt32();
622            break;
623        case MTP_TYPE_INT64:
624            longValue = packet.getInt64();
625            break;
626        case MTP_TYPE_UINT64:
627            longValue = packet.getUInt64();
628            break;
629        case MTP_TYPE_STR:
630        {
631            MtpStringBuffer buffer;
632            packet.getString(buffer);
633            stringValue = env->NewStringUTF((const char *)buffer);
634            break;
635         }
636        default:
637            LOGE("unsupported type in setDevicePropertyValue\n");
638            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
639    }
640
641    jint result = env->CallIntMethod(mDatabase, method_setDeviceProperty,
642                (jint)property, longValue, stringValue);
643    if (stringValue)
644        env->DeleteLocalRef(stringValue);
645
646    checkAndClearExceptionFromCallback(env, __FUNCTION__);
647    return result;
648}
649
650MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) {
651    return -1;
652}
653
654MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle,
655                                            uint32_t format, uint32_t property,
656                                            int groupCode, int depth,
657                                            MtpDataPacket& packet) {
658    JNIEnv* env = AndroidRuntime::getJNIEnv();
659    jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList,
660                (jlong)handle, (jint)format, (jlong)property, (jint)groupCode, (jint)depth);
661    checkAndClearExceptionFromCallback(env, __FUNCTION__);
662    if (!list)
663        return MTP_RESPONSE_GENERAL_ERROR;
664    int count = env->GetIntField(list, field_mCount);
665    MtpResponseCode result = env->GetIntField(list, field_mResult);
666
667    packet.putUInt32(count);
668    if (count > 0) {
669        jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles);
670        jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes);
671        jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes);
672        jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues);
673        jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues);
674
675        jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0);
676        jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0);
677        jint* dataTypes = env->GetIntArrayElements(dataTypesArray, 0);
678        jlong* longValues = (longValuesArray ? env->GetLongArrayElements(longValuesArray, 0) : NULL);
679
680        for (int i = 0; i < count; i++) {
681            packet.putUInt32(objectHandles[i]);
682            packet.putUInt16(propertyCodes[i]);
683            int type = dataTypes[i];
684            packet.putUInt16(type);
685
686            switch (type) {
687                case MTP_TYPE_INT8:
688                    packet.putInt8(longValues[i]);
689                    break;
690                case MTP_TYPE_UINT8:
691                    packet.putUInt8(longValues[i]);
692                    break;
693                case MTP_TYPE_INT16:
694                    packet.putInt16(longValues[i]);
695                    break;
696                case MTP_TYPE_UINT16:
697                    packet.putUInt16(longValues[i]);
698                    break;
699                case MTP_TYPE_INT32:
700                    packet.putInt32(longValues[i]);
701                    break;
702                case MTP_TYPE_UINT32:
703                    packet.putUInt32(longValues[i]);
704                    break;
705                case MTP_TYPE_INT64:
706                    packet.putInt64(longValues[i]);
707                    break;
708                case MTP_TYPE_UINT64:
709                    packet.putUInt64(longValues[i]);
710                    break;
711                case MTP_TYPE_INT128:
712                    packet.putInt128(longValues[i]);
713                    break;
714                case MTP_TYPE_UINT128:
715                    packet.putUInt128(longValues[i]);
716                    break;
717                case MTP_TYPE_STR: {
718                    jstring value = (jstring)env->GetObjectArrayElement(stringValuesArray, i);
719                    const char *valueStr = (value ? env->GetStringUTFChars(value, NULL) : NULL);
720                    if (valueStr) {
721                        packet.putString(valueStr);
722                        env->ReleaseStringUTFChars(value, valueStr);
723                    } else {
724                        packet.putEmptyString();
725                    }
726                    env->DeleteLocalRef(value);
727                    break;
728                }
729                default:
730                    LOGE("bad or unsupported data type in MyMtpDatabase::getObjectPropertyList");
731                    break;
732            }
733        }
734
735        env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0);
736        env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0);
737        env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0);
738        if (longValues)
739            env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
740
741        env->DeleteLocalRef(objectHandlesArray);
742        env->DeleteLocalRef(propertyCodesArray);
743        env->DeleteLocalRef(dataTypesArray);
744        if (longValuesArray)
745            env->DeleteLocalRef(longValuesArray);
746        if (stringValuesArray)
747            env->DeleteLocalRef(stringValuesArray);
748    }
749
750    env->DeleteLocalRef(list);
751    checkAndClearExceptionFromCallback(env, __FUNCTION__);
752    return result;
753}
754
755MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
756                                            MtpObjectInfo& info) {
757    char    date[20];
758
759    JNIEnv* env = AndroidRuntime::getJNIEnv();
760    jboolean result = env->CallBooleanMethod(mDatabase, method_getObjectInfo,
761                (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer);
762    if (!result)
763        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
764
765    jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
766    info.mStorageID = intValues[0];
767    info.mFormat = intValues[1];
768    info.mParent = intValues[2];
769    env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
770
771    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
772    uint64_t size = longValues[0];
773    info.mCompressedSize = (size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size);
774    info.mDateModified = longValues[1];
775    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
776
777//    info.mAssociationType = (format == MTP_FORMAT_ASSOCIATION ?
778//                            MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
779//                            MTP_ASSOCIATION_TYPE_UNDEFINED);
780    info.mAssociationType = MTP_ASSOCIATION_TYPE_UNDEFINED;
781
782    jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
783    MtpString temp(str);
784    info.mName = strdup((const char *)temp);
785    env->ReleaseCharArrayElements(mStringBuffer, str, 0);
786
787    // read EXIF data for thumbnail information
788    if (info.mFormat == MTP_FORMAT_EXIF_JPEG || info.mFormat == MTP_FORMAT_JFIF) {
789        MtpString path;
790        int64_t length;
791        MtpObjectFormat format;
792        if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK) {
793            ResetJpgfile();
794             // Start with an empty image information structure.
795            memset(&ImageInfo, 0, sizeof(ImageInfo));
796            ImageInfo.FlashUsed = -1;
797            ImageInfo.MeteringMode = -1;
798            ImageInfo.Whitebalance = -1;
799            strncpy(ImageInfo.FileName, (const char *)path, PATH_MAX);
800            if (ReadJpegFile((const char*)path, READ_METADATA)) {
801                Section_t* section = FindSection(M_EXIF);
802                if (section) {
803                    info.mThumbCompressedSize = ImageInfo.ThumbnailSize;
804                    info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
805                    info.mImagePixWidth = ImageInfo.Width;
806                    info.mImagePixHeight = ImageInfo.Height;
807                }
808            }
809            DiscardData();
810        }
811    }
812
813    checkAndClearExceptionFromCallback(env, __FUNCTION__);
814    return MTP_RESPONSE_OK;
815}
816
817void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
818    MtpString path;
819    int64_t length;
820    MtpObjectFormat format;
821    void* result = NULL;
822    outThumbSize = 0;
823
824    if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK
825            && (format == MTP_FORMAT_EXIF_JPEG || format == MTP_FORMAT_JFIF)) {
826        ResetJpgfile();
827         // Start with an empty image information structure.
828        memset(&ImageInfo, 0, sizeof(ImageInfo));
829        ImageInfo.FlashUsed = -1;
830        ImageInfo.MeteringMode = -1;
831        ImageInfo.Whitebalance = -1;
832        strncpy(ImageInfo.FileName, (const char *)path, PATH_MAX);
833        if (ReadJpegFile((const char*)path, READ_METADATA)) {
834            Section_t* section = FindSection(M_EXIF);
835            if (section) {
836                outThumbSize = ImageInfo.ThumbnailSize;
837                result = malloc(outThumbSize);
838                if (result)
839                    memcpy(result, section->Data + ImageInfo.ThumbnailOffset + 8, outThumbSize);
840            }
841            DiscardData();
842        }
843    }
844
845    return result;
846}
847
848MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle,
849                                            MtpString& outFilePath,
850                                            int64_t& outFileLength,
851                                            MtpObjectFormat& outFormat) {
852    JNIEnv* env = AndroidRuntime::getJNIEnv();
853    jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath,
854                (jint)handle, mStringBuffer, mLongBuffer);
855    if (result != MTP_RESPONSE_OK) {
856        checkAndClearExceptionFromCallback(env, __FUNCTION__);
857        return result;
858    }
859
860    jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
861    outFilePath.setTo(str, strlen16(str));
862    env->ReleaseCharArrayElements(mStringBuffer, str, 0);
863
864    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
865    outFileLength = longValues[0];
866    outFormat = longValues[1];
867    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
868
869    checkAndClearExceptionFromCallback(env, __FUNCTION__);
870    return result;
871}
872
873MtpResponseCode MyMtpDatabase::deleteFile(MtpObjectHandle handle) {
874    JNIEnv* env = AndroidRuntime::getJNIEnv();
875    MtpResponseCode result = env->CallIntMethod(mDatabase, method_deleteFile, (jint)handle);
876
877    checkAndClearExceptionFromCallback(env, __FUNCTION__);
878    return result;
879}
880
881struct PropertyTableEntry {
882    MtpObjectProperty   property;
883    int                 type;
884};
885
886static const PropertyTableEntry   kObjectPropertyTable[] = {
887    {   MTP_PROPERTY_STORAGE_ID,        MTP_TYPE_UINT32     },
888    {   MTP_PROPERTY_OBJECT_FORMAT,     MTP_TYPE_UINT16     },
889    {   MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16     },
890    {   MTP_PROPERTY_OBJECT_SIZE,       MTP_TYPE_UINT64     },
891    {   MTP_PROPERTY_OBJECT_FILE_NAME,  MTP_TYPE_STR        },
892    {   MTP_PROPERTY_DATE_MODIFIED,     MTP_TYPE_STR        },
893    {   MTP_PROPERTY_PARENT_OBJECT,     MTP_TYPE_UINT32     },
894    {   MTP_PROPERTY_PERSISTENT_UID,    MTP_TYPE_UINT128    },
895    {   MTP_PROPERTY_NAME,              MTP_TYPE_STR        },
896    {   MTP_PROPERTY_DISPLAY_NAME,      MTP_TYPE_STR        },
897    {   MTP_PROPERTY_DATE_ADDED,        MTP_TYPE_STR        },
898    {   MTP_PROPERTY_ARTIST,            MTP_TYPE_STR        },
899    {   MTP_PROPERTY_ALBUM_NAME,        MTP_TYPE_STR        },
900    {   MTP_PROPERTY_ALBUM_ARTIST,      MTP_TYPE_STR        },
901    {   MTP_PROPERTY_TRACK,             MTP_TYPE_UINT16     },
902    {   MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_TYPE_STR    },
903    {   MTP_PROPERTY_GENRE,             MTP_TYPE_STR        },
904    {   MTP_PROPERTY_COMPOSER,          MTP_TYPE_STR        },
905    {   MTP_PROPERTY_DURATION,          MTP_TYPE_UINT32     },
906    {   MTP_PROPERTY_DESCRIPTION,       MTP_TYPE_STR        },
907};
908
909static const PropertyTableEntry   kDevicePropertyTable[] = {
910    {   MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,    MTP_TYPE_STR },
911    {   MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,       MTP_TYPE_STR },
912    {   MTP_DEVICE_PROPERTY_IMAGE_SIZE,                 MTP_TYPE_STR },
913};
914
915bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
916    int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
917    const PropertyTableEntry* entry = kObjectPropertyTable;
918    for (int i = 0; i < count; i++, entry++) {
919        if (entry->property == property) {
920            type = entry->type;
921            return true;
922        }
923    }
924    return false;
925}
926
927bool MyMtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
928    int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]);
929    const PropertyTableEntry* entry = kDevicePropertyTable;
930    for (int i = 0; i < count; i++, entry++) {
931        if (entry->property == property) {
932            type = entry->type;
933            return true;
934        }
935    }
936    return false;
937}
938
939MtpObjectHandleList* MyMtpDatabase::getObjectReferences(MtpObjectHandle handle) {
940    JNIEnv* env = AndroidRuntime::getJNIEnv();
941    jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectReferences,
942                (jint)handle);
943    if (!array)
944        return NULL;
945    MtpObjectHandleList* list = new MtpObjectHandleList();
946    jint* handles = env->GetIntArrayElements(array, 0);
947    jsize length = env->GetArrayLength(array);
948    for (int i = 0; i < length; i++)
949        list->push(handles[i]);
950    env->ReleaseIntArrayElements(array, handles, 0);
951    env->DeleteLocalRef(array);
952
953    checkAndClearExceptionFromCallback(env, __FUNCTION__);
954    return list;
955}
956
957MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle,
958                                                    MtpObjectHandleList* references) {
959    JNIEnv* env = AndroidRuntime::getJNIEnv();
960    int count = references->size();
961    jintArray array = env->NewIntArray(count);
962    if (!array) {
963        LOGE("out of memory in setObjectReferences");
964        return false;
965    }
966    jint* handles = env->GetIntArrayElements(array, 0);
967     for (int i = 0; i < count; i++)
968        handles[i] = (*references)[i];
969    env->ReleaseIntArrayElements(array, handles, 0);
970    MtpResponseCode result = env->CallIntMethod(mDatabase, method_setObjectReferences,
971                (jint)handle, array);
972    env->DeleteLocalRef(array);
973
974    checkAndClearExceptionFromCallback(env, __FUNCTION__);
975    return result;
976}
977
978MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
979                                            MtpObjectFormat format) {
980    MtpProperty* result = NULL;
981    switch (property) {
982        case MTP_PROPERTY_OBJECT_FORMAT:
983            // use format as default value
984            result = new MtpProperty(property, MTP_TYPE_UINT16, false, format);
985            break;
986        case MTP_PROPERTY_PROTECTION_STATUS:
987        case MTP_PROPERTY_TRACK:
988            result = new MtpProperty(property, MTP_TYPE_UINT16);
989            break;
990        case MTP_PROPERTY_STORAGE_ID:
991        case MTP_PROPERTY_PARENT_OBJECT:
992        case MTP_PROPERTY_DURATION:
993            result = new MtpProperty(property, MTP_TYPE_UINT32);
994            break;
995        case MTP_PROPERTY_OBJECT_SIZE:
996            result = new MtpProperty(property, MTP_TYPE_UINT64);
997            break;
998        case MTP_PROPERTY_PERSISTENT_UID:
999            result = new MtpProperty(property, MTP_TYPE_UINT128);
1000            break;
1001        case MTP_PROPERTY_NAME:
1002        case MTP_PROPERTY_DISPLAY_NAME:
1003        case MTP_PROPERTY_ARTIST:
1004        case MTP_PROPERTY_ALBUM_NAME:
1005        case MTP_PROPERTY_ALBUM_ARTIST:
1006        case MTP_PROPERTY_GENRE:
1007        case MTP_PROPERTY_COMPOSER:
1008        case MTP_PROPERTY_DESCRIPTION:
1009            result = new MtpProperty(property, MTP_TYPE_STR);
1010            break;
1011        case MTP_PROPERTY_DATE_MODIFIED:
1012        case MTP_PROPERTY_DATE_ADDED:
1013        case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
1014            result = new MtpProperty(property, MTP_TYPE_STR);
1015            result->setFormDateTime();
1016            break;
1017        case MTP_PROPERTY_OBJECT_FILE_NAME:
1018            // We allow renaming files and folders
1019            result = new MtpProperty(property, MTP_TYPE_STR, true);
1020            break;
1021    }
1022
1023    return result;
1024}
1025
1026MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
1027    JNIEnv* env = AndroidRuntime::getJNIEnv();
1028    MtpProperty* result = NULL;
1029    bool writable = false;
1030
1031    switch (property) {
1032        case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
1033        case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
1034            writable = true;
1035            // fall through
1036        case MTP_DEVICE_PROPERTY_IMAGE_SIZE:
1037            result = new MtpProperty(property, MTP_TYPE_STR, writable);
1038
1039            // get current value
1040            jint ret = env->CallIntMethod(mDatabase, method_getDeviceProperty,
1041                        (jint)property, mLongBuffer, mStringBuffer);
1042            if (ret == MTP_RESPONSE_OK) {
1043                jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
1044                result->setCurrentValue(str);
1045                // for read-only properties it is safe to assume current value is default value
1046                if (!writable)
1047                    result->setDefaultValue(str);
1048                env->ReleaseCharArrayElements(mStringBuffer, str, 0);
1049            } else {
1050                LOGE("unable to read device property, response: %04X", ret);
1051            }
1052            break;
1053    }
1054
1055    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1056    return result;
1057}
1058
1059void MyMtpDatabase::sessionStarted() {
1060    JNIEnv* env = AndroidRuntime::getJNIEnv();
1061    env->CallVoidMethod(mDatabase, method_sessionStarted);
1062    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1063}
1064
1065void MyMtpDatabase::sessionEnded() {
1066    JNIEnv* env = AndroidRuntime::getJNIEnv();
1067    env->CallVoidMethod(mDatabase, method_sessionEnded);
1068    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1069}
1070
1071#endif // HAVE_ANDROID_OS
1072
1073// ----------------------------------------------------------------------------
1074
1075static void
1076android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz)
1077{
1078#ifdef HAVE_ANDROID_OS
1079    MyMtpDatabase* database = new MyMtpDatabase(env, thiz);
1080    env->SetIntField(thiz, field_context, (int)database);
1081    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1082#endif
1083}
1084
1085static void
1086android_mtp_MtpDatabase_finalize(JNIEnv *env, jobject thiz)
1087{
1088#ifdef HAVE_ANDROID_OS
1089    MyMtpDatabase* database = (MyMtpDatabase *)env->GetIntField(thiz, field_context);
1090    database->cleanup(env);
1091    delete database;
1092    env->SetIntField(thiz, field_context, 0);
1093    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1094#endif
1095}
1096
1097static jstring
1098android_mtp_MtpPropertyGroup_format_date_time(JNIEnv *env, jobject thiz, jlong seconds)
1099{
1100#ifdef HAVE_ANDROID_OS
1101    char    date[20];
1102    formatDateTime(seconds, date, sizeof(date));
1103    return env->NewStringUTF(date);
1104#else
1105    return NULL;
1106#endif
1107}
1108
1109// ----------------------------------------------------------------------------
1110
1111static JNINativeMethod gMtpDatabaseMethods[] = {
1112    {"native_setup",            "()V",  (void *)android_mtp_MtpDatabase_setup},
1113    {"native_finalize",         "()V",  (void *)android_mtp_MtpDatabase_finalize},
1114};
1115
1116static JNINativeMethod gMtpPropertyGroupMethods[] = {
1117    {"format_date_time",        "(J)Ljava/lang/String;",
1118                                        (void *)android_mtp_MtpPropertyGroup_format_date_time},
1119};
1120
1121static const char* const kClassPathName = "android/mtp/MtpDatabase";
1122
1123int register_android_mtp_MtpDatabase(JNIEnv *env)
1124{
1125    jclass clazz;
1126
1127    clazz = env->FindClass("android/mtp/MtpDatabase");
1128    if (clazz == NULL) {
1129        LOGE("Can't find android/mtp/MtpDatabase");
1130        return -1;
1131    }
1132    method_beginSendObject = env->GetMethodID(clazz, "beginSendObject", "(Ljava/lang/String;IIIJJ)I");
1133    if (method_beginSendObject == NULL) {
1134        LOGE("Can't find beginSendObject");
1135        return -1;
1136    }
1137    method_endSendObject = env->GetMethodID(clazz, "endSendObject", "(Ljava/lang/String;IIZ)V");
1138    if (method_endSendObject == NULL) {
1139        LOGE("Can't find endSendObject");
1140        return -1;
1141    }
1142    method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I");
1143    if (method_getObjectList == NULL) {
1144        LOGE("Can't find getObjectList");
1145        return -1;
1146    }
1147    method_getNumObjects = env->GetMethodID(clazz, "getNumObjects", "(III)I");
1148    if (method_getNumObjects == NULL) {
1149        LOGE("Can't find getNumObjects");
1150        return -1;
1151    }
1152    method_getSupportedPlaybackFormats = env->GetMethodID(clazz, "getSupportedPlaybackFormats", "()[I");
1153    if (method_getSupportedPlaybackFormats == NULL) {
1154        LOGE("Can't find getSupportedPlaybackFormats");
1155        return -1;
1156    }
1157    method_getSupportedCaptureFormats = env->GetMethodID(clazz, "getSupportedCaptureFormats", "()[I");
1158    if (method_getSupportedCaptureFormats == NULL) {
1159        LOGE("Can't find getSupportedCaptureFormats");
1160        return -1;
1161    }
1162    method_getSupportedObjectProperties = env->GetMethodID(clazz, "getSupportedObjectProperties", "(I)[I");
1163    if (method_getSupportedObjectProperties == NULL) {
1164        LOGE("Can't find getSupportedObjectProperties");
1165        return -1;
1166    }
1167    method_getSupportedDeviceProperties = env->GetMethodID(clazz, "getSupportedDeviceProperties", "()[I");
1168    if (method_getSupportedDeviceProperties == NULL) {
1169        LOGE("Can't find getSupportedDeviceProperties");
1170        return -1;
1171    }
1172    method_setObjectProperty = env->GetMethodID(clazz, "setObjectProperty", "(IIJLjava/lang/String;)I");
1173    if (method_setObjectProperty == NULL) {
1174        LOGE("Can't find setObjectProperty");
1175        return -1;
1176    }
1177    method_getDeviceProperty = env->GetMethodID(clazz, "getDeviceProperty", "(I[J[C)I");
1178    if (method_getDeviceProperty == NULL) {
1179        LOGE("Can't find getDeviceProperty");
1180        return -1;
1181    }
1182    method_setDeviceProperty = env->GetMethodID(clazz, "setDeviceProperty", "(IJLjava/lang/String;)I");
1183    if (method_setDeviceProperty == NULL) {
1184        LOGE("Can't find setDeviceProperty");
1185        return -1;
1186    }
1187    method_getObjectPropertyList = env->GetMethodID(clazz, "getObjectPropertyList",
1188            "(JIJII)Landroid/mtp/MtpPropertyList;");
1189    if (method_getObjectPropertyList == NULL) {
1190        LOGE("Can't find getObjectPropertyList");
1191        return -1;
1192    }
1193    method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z");
1194    if (method_getObjectInfo == NULL) {
1195        LOGE("Can't find getObjectInfo");
1196        return -1;
1197    }
1198    method_getObjectFilePath = env->GetMethodID(clazz, "getObjectFilePath", "(I[C[J)I");
1199    if (method_getObjectFilePath == NULL) {
1200        LOGE("Can't find getObjectFilePath");
1201        return -1;
1202    }
1203    method_deleteFile = env->GetMethodID(clazz, "deleteFile", "(I)I");
1204    if (method_deleteFile == NULL) {
1205        LOGE("Can't find deleteFile");
1206        return -1;
1207    }
1208    method_getObjectReferences = env->GetMethodID(clazz, "getObjectReferences", "(I)[I");
1209    if (method_getObjectReferences == NULL) {
1210        LOGE("Can't find getObjectReferences");
1211        return -1;
1212    }
1213    method_setObjectReferences = env->GetMethodID(clazz, "setObjectReferences", "(I[I)I");
1214    if (method_setObjectReferences == NULL) {
1215        LOGE("Can't find setObjectReferences");
1216        return -1;
1217    }
1218    method_sessionStarted = env->GetMethodID(clazz, "sessionStarted", "()V");
1219    if (method_sessionStarted == NULL) {
1220        LOGE("Can't find sessionStarted");
1221        return -1;
1222    }
1223    method_sessionEnded = env->GetMethodID(clazz, "sessionEnded", "()V");
1224    if (method_sessionEnded == NULL) {
1225        LOGE("Can't find sessionEnded");
1226        return -1;
1227    }
1228
1229    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
1230    if (field_context == NULL) {
1231        LOGE("Can't find MtpDatabase.mNativeContext");
1232        return -1;
1233    }
1234
1235    // now set up fields for MtpPropertyList class
1236    clazz = env->FindClass("android/mtp/MtpPropertyList");
1237    if (clazz == NULL) {
1238        LOGE("Can't find android/mtp/MtpPropertyList");
1239        return -1;
1240    }
1241    field_mCount = env->GetFieldID(clazz, "mCount", "I");
1242    if (field_mCount == NULL) {
1243        LOGE("Can't find MtpPropertyList.mCount");
1244        return -1;
1245    }
1246    field_mResult = env->GetFieldID(clazz, "mResult", "I");
1247    if (field_mResult == NULL) {
1248        LOGE("Can't find MtpPropertyList.mResult");
1249        return -1;
1250    }
1251    field_mObjectHandles = env->GetFieldID(clazz, "mObjectHandles", "[I");
1252    if (field_mObjectHandles == NULL) {
1253        LOGE("Can't find MtpPropertyList.mObjectHandles");
1254        return -1;
1255    }
1256    field_mPropertyCodes = env->GetFieldID(clazz, "mPropertyCodes", "[I");
1257    if (field_mPropertyCodes == NULL) {
1258        LOGE("Can't find MtpPropertyList.mPropertyCodes");
1259        return -1;
1260    }
1261    field_mDataTypes = env->GetFieldID(clazz, "mDataTypes", "[I");
1262    if (field_mDataTypes == NULL) {
1263        LOGE("Can't find MtpPropertyList.mDataTypes");
1264        return -1;
1265    }
1266    field_mLongValues = env->GetFieldID(clazz, "mLongValues", "[J");
1267    if (field_mLongValues == NULL) {
1268        LOGE("Can't find MtpPropertyList.mLongValues");
1269        return -1;
1270    }
1271    field_mStringValues = env->GetFieldID(clazz, "mStringValues", "[Ljava/lang/String;");
1272    if (field_mStringValues == NULL) {
1273        LOGE("Can't find MtpPropertyList.mStringValues");
1274        return -1;
1275    }
1276
1277    if (AndroidRuntime::registerNativeMethods(env,
1278                "android/mtp/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods)))
1279        return -1;
1280
1281    return AndroidRuntime::registerNativeMethods(env,
1282                "android/mtp/MtpPropertyGroup", gMtpPropertyGroupMethods, NELEM(gMtpPropertyGroupMethods));
1283}
1284