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