android_mtp_MtpDatabase.cpp revision 0cd0136d440cf6ad9d5fab430269116786e671ec
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 "MtpProperty.h"
33#include "MtpStringBuffer.h"
34#include "MtpUtils.h"
35#include "mtp.h"
36
37using namespace android;
38
39// ----------------------------------------------------------------------------
40
41static jmethodID method_beginSendObject;
42static jmethodID method_endSendObject;
43static jmethodID method_getObjectList;
44static jmethodID method_getNumObjects;
45static jmethodID method_getSupportedPlaybackFormats;
46static jmethodID method_getSupportedCaptureFormats;
47static jmethodID method_getSupportedObjectProperties;
48static jmethodID method_getSupportedDeviceProperties;
49static jmethodID method_setObjectProperty;
50static jmethodID method_getDeviceProperty;
51static jmethodID method_setDeviceProperty;
52static jmethodID method_getObjectPropertyList;
53static jmethodID method_getObjectInfo;
54static jmethodID method_getObjectFilePath;
55static jmethodID method_deleteFile;
56static jmethodID method_getObjectReferences;
57static jmethodID method_setObjectReferences;
58static jmethodID method_sessionStarted;
59static jmethodID method_sessionEnded;
60
61static jfieldID field_context;
62
63// MtpPropertyList fields
64static jfieldID field_mCount;
65static jfieldID field_mResult;
66static jfieldID field_mObjectHandles;
67static jfieldID field_mPropertyCodes;
68static jfieldID field_mDataTypes;
69static jfieldID field_mLongValues;
70static jfieldID field_mStringValues;
71
72
73MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
74    return (MtpDatabase *)env->GetIntField(database, field_context);
75}
76
77#ifdef HAVE_ANDROID_OS
78// ----------------------------------------------------------------------------
79
80class MyMtpDatabase : public MtpDatabase {
81private:
82    jobject         mDatabase;
83    jintArray       mIntBuffer;
84    jlongArray      mLongBuffer;
85    jcharArray      mStringBuffer;
86
87public:
88                                    MyMtpDatabase(JNIEnv *env, jobject client);
89    virtual                         ~MyMtpDatabase();
90    void                            cleanup(JNIEnv *env);
91
92    virtual MtpObjectHandle         beginSendObject(const char* path,
93                                            MtpObjectFormat format,
94                                            MtpObjectHandle parent,
95                                            MtpStorageID storage,
96                                            uint64_t size,
97                                            time_t modified);
98
99    virtual void                    endSendObject(const char* path,
100                                            MtpObjectHandle handle,
101                                            MtpObjectFormat format,
102                                            int64_t actualSize,
103                                            bool succeeded);
104
105    virtual MtpObjectHandleList*    getObjectList(MtpStorageID storageID,
106                                    MtpObjectFormat format,
107                                    MtpObjectHandle parent);
108
109    virtual int                     getNumObjects(MtpStorageID storageID,
110                                            MtpObjectFormat format,
111                                            MtpObjectHandle parent);
112
113    // callee should delete[] the results from these
114    // results can be NULL
115    virtual MtpObjectFormatList*    getSupportedPlaybackFormats();
116    virtual MtpObjectFormatList*    getSupportedCaptureFormats();
117    virtual MtpObjectPropertyList*  getSupportedObjectProperties(MtpObjectFormat format);
118    virtual MtpDevicePropertyList*  getSupportedDeviceProperties();
119
120    virtual MtpResponseCode         getObjectPropertyValue(MtpObjectHandle handle,
121                                            MtpObjectProperty property,
122                                            MtpDataPacket& packet);
123
124    virtual MtpResponseCode         setObjectPropertyValue(MtpObjectHandle handle,
125                                            MtpObjectProperty property,
126                                            MtpDataPacket& packet);
127
128    virtual MtpResponseCode         getDevicePropertyValue(MtpDeviceProperty property,
129                                            MtpDataPacket& packet);
130
131    virtual MtpResponseCode         setDevicePropertyValue(MtpDeviceProperty property,
132                                            MtpDataPacket& packet);
133
134    virtual MtpResponseCode         resetDeviceProperty(MtpDeviceProperty property);
135
136    virtual MtpResponseCode         getObjectPropertyList(MtpObjectHandle handle,
137                                            uint32_t format, uint32_t property,
138                                            int groupCode, int depth,
139                                            MtpDataPacket& packet);
140
141    virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
142                                            MtpDataPacket& packet);
143
144    virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
145                                            MtpString& outFilePath,
146                                            int64_t& outFileLength,
147                                            MtpObjectFormat& outFormat);
148    virtual MtpResponseCode         deleteFile(MtpObjectHandle handle);
149
150    bool                            getObjectPropertyInfo(MtpObjectProperty property, int& type);
151    bool                            getDevicePropertyInfo(MtpDeviceProperty property, int& type);
152
153    virtual MtpObjectHandleList*    getObjectReferences(MtpObjectHandle handle);
154
155    virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
156                                            MtpObjectHandleList* references);
157
158    virtual MtpProperty*            getObjectPropertyDesc(MtpObjectProperty property,
159                                            MtpObjectFormat format);
160
161    virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property);
162
163    virtual void                    sessionStarted();
164
165    virtual void                    sessionEnded();
166};
167
168// ----------------------------------------------------------------------------
169
170static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
171    if (env->ExceptionCheck()) {
172        LOGE("An exception was thrown by callback '%s'.", methodName);
173        LOGE_EX(env);
174        env->ExceptionClear();
175    }
176}
177
178// ----------------------------------------------------------------------------
179
180MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client)
181    :   mDatabase(env->NewGlobalRef(client)),
182        mIntBuffer(NULL),
183        mLongBuffer(NULL),
184        mStringBuffer(NULL)
185{
186    jintArray intArray;
187    jlongArray longArray;
188    jcharArray charArray;
189
190    // create buffers for out arguments
191    // we don't need to be thread-safe so this is OK
192    intArray = env->NewIntArray(3);
193    if (!intArray)
194        goto out_of_memory;
195    mIntBuffer = (jintArray)env->NewGlobalRef(intArray);
196    longArray = env->NewLongArray(2);
197    if (!longArray)
198        goto out_of_memory;
199    mLongBuffer = (jlongArray)env->NewGlobalRef(longArray);
200    charArray = env->NewCharArray(256);
201    if (!charArray)
202        goto out_of_memory;
203    mStringBuffer = (jcharArray)env->NewGlobalRef(charArray);
204    return;
205
206out_of_memory:
207    env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), NULL);
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, int64_t actualSize, 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, (jlong)actualSize, (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                    packet.putString(str);
432                    env->ReleaseStringUTFChars(stringValue, str);
433                } else {
434                    packet.putEmptyString();
435                }
436                break;
437             }
438            default:
439                LOGE("unsupported type in getObjectPropertyValue\n");
440                result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
441        }
442out:
443        env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0);
444        env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0);
445        env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0);
446        if (longValues)
447            env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
448
449        env->DeleteLocalRef(objectHandlesArray);
450        env->DeleteLocalRef(propertyCodesArray);
451        env->DeleteLocalRef(dataTypesArray);
452        if (longValuesArray)
453            env->DeleteLocalRef(longValuesArray);
454        if (stringValuesArray)
455            env->DeleteLocalRef(stringValuesArray);
456    }
457
458    env->DeleteLocalRef(list);
459    checkAndClearExceptionFromCallback(env, __FUNCTION__);
460    return result;
461}
462
463MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
464                                            MtpObjectProperty property,
465                                            MtpDataPacket& packet) {
466    int         type;
467
468    if (!getObjectPropertyInfo(property, type))
469        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
470
471    JNIEnv* env = AndroidRuntime::getJNIEnv();
472    jlong longValue = 0;
473    jstring stringValue = NULL;
474
475    switch (type) {
476        case MTP_TYPE_INT8:
477            longValue = packet.getInt8();
478            break;
479        case MTP_TYPE_UINT8:
480            longValue = packet.getUInt8();
481            break;
482        case MTP_TYPE_INT16:
483            longValue = packet.getInt16();
484            break;
485        case MTP_TYPE_UINT16:
486            longValue = packet.getUInt16();
487            break;
488        case MTP_TYPE_INT32:
489            longValue = packet.getInt32();
490            break;
491        case MTP_TYPE_UINT32:
492            longValue = packet.getUInt32();
493            break;
494        case MTP_TYPE_INT64:
495            longValue = packet.getInt64();
496            break;
497        case MTP_TYPE_UINT64:
498            longValue = packet.getUInt64();
499            break;
500        case MTP_TYPE_STR:
501        {
502            MtpStringBuffer buffer;
503            packet.getString(buffer);
504            stringValue = env->NewStringUTF((const char *)buffer);
505            break;
506         }
507        default:
508            LOGE("unsupported type in getObjectPropertyValue\n");
509            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
510    }
511
512    jint result = env->CallIntMethod(mDatabase, method_setObjectProperty,
513                (jint)handle, (jint)property, longValue, stringValue);
514    if (stringValue)
515        env->DeleteLocalRef(stringValue);
516
517    checkAndClearExceptionFromCallback(env, __FUNCTION__);
518    return result;
519}
520
521MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
522                                            MtpDataPacket& packet) {
523    int         type;
524
525    if (!getDevicePropertyInfo(property, type))
526        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
527
528    JNIEnv* env = AndroidRuntime::getJNIEnv();
529    jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty,
530                (jint)property, mLongBuffer, mStringBuffer);
531    if (result != MTP_RESPONSE_OK) {
532        checkAndClearExceptionFromCallback(env, __FUNCTION__);
533        return result;
534    }
535
536    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
537    jlong longValue = longValues[0];
538    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
539
540    switch (type) {
541        case MTP_TYPE_INT8:
542            packet.putInt8(longValue);
543            break;
544        case MTP_TYPE_UINT8:
545            packet.putUInt8(longValue);
546            break;
547        case MTP_TYPE_INT16:
548            packet.putInt16(longValue);
549            break;
550        case MTP_TYPE_UINT16:
551            packet.putUInt16(longValue);
552            break;
553        case MTP_TYPE_INT32:
554            packet.putInt32(longValue);
555            break;
556        case MTP_TYPE_UINT32:
557            packet.putUInt32(longValue);
558            break;
559        case MTP_TYPE_INT64:
560            packet.putInt64(longValue);
561            break;
562        case MTP_TYPE_UINT64:
563            packet.putUInt64(longValue);
564            break;
565        case MTP_TYPE_INT128:
566            packet.putInt128(longValue);
567            break;
568        case MTP_TYPE_UINT128:
569            packet.putInt128(longValue);
570            break;
571        case MTP_TYPE_STR:
572        {
573            jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
574            packet.putString(str);
575            env->ReleaseCharArrayElements(mStringBuffer, str, 0);
576            break;
577         }
578        default:
579            LOGE("unsupported type in getDevicePropertyValue\n");
580            return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
581    }
582
583    checkAndClearExceptionFromCallback(env, __FUNCTION__);
584    return MTP_RESPONSE_OK;
585}
586
587MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property,
588                                            MtpDataPacket& packet) {
589    int         type;
590
591    if (!getDevicePropertyInfo(property, type))
592        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
593
594    JNIEnv* env = AndroidRuntime::getJNIEnv();
595    jlong longValue = 0;
596    jstring stringValue = NULL;
597
598    switch (type) {
599        case MTP_TYPE_INT8:
600            longValue = packet.getInt8();
601            break;
602        case MTP_TYPE_UINT8:
603            longValue = packet.getUInt8();
604            break;
605        case MTP_TYPE_INT16:
606            longValue = packet.getInt16();
607            break;
608        case MTP_TYPE_UINT16:
609            longValue = packet.getUInt16();
610            break;
611        case MTP_TYPE_INT32:
612            longValue = packet.getInt32();
613            break;
614        case MTP_TYPE_UINT32:
615            longValue = packet.getUInt32();
616            break;
617        case MTP_TYPE_INT64:
618            longValue = packet.getInt64();
619            break;
620        case MTP_TYPE_UINT64:
621            longValue = packet.getUInt64();
622            break;
623        case MTP_TYPE_STR:
624        {
625            MtpStringBuffer buffer;
626            packet.getString(buffer);
627            stringValue = env->NewStringUTF((const char *)buffer);
628            break;
629         }
630        default:
631            LOGE("unsupported type in setDevicePropertyValue\n");
632            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
633    }
634
635    jint result = env->CallIntMethod(mDatabase, method_setDeviceProperty,
636                (jint)property, longValue, stringValue);
637    if (stringValue)
638        env->DeleteLocalRef(stringValue);
639
640    checkAndClearExceptionFromCallback(env, __FUNCTION__);
641    return result;
642}
643
644MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) {
645    return -1;
646}
647
648MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle,
649                                            uint32_t format, uint32_t property,
650                                            int groupCode, int depth,
651                                            MtpDataPacket& packet) {
652    JNIEnv* env = AndroidRuntime::getJNIEnv();
653    jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList,
654                (jlong)handle, (jint)format, (jlong)property, (jint)groupCode, (jint)depth);
655    checkAndClearExceptionFromCallback(env, __FUNCTION__);
656    if (!list)
657        return MTP_RESPONSE_GENERAL_ERROR;
658    int count = env->GetIntField(list, field_mCount);
659    MtpResponseCode result = env->GetIntField(list, field_mResult);
660
661    packet.putUInt32(count);
662    if (count > 0) {
663        jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles);
664        jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes);
665        jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes);
666        jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues);
667        jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues);
668
669        jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0);
670        jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0);
671        jint* dataTypes = env->GetIntArrayElements(dataTypesArray, 0);
672        jlong* longValues = (longValuesArray ? env->GetLongArrayElements(longValuesArray, 0) : NULL);
673
674        for (int i = 0; i < count; i++) {
675            packet.putUInt32(objectHandles[i]);
676            packet.putUInt16(propertyCodes[i]);
677            int type = dataTypes[i];
678            packet.putUInt16(type);
679
680            switch (type) {
681                case MTP_TYPE_INT8:
682                    packet.putInt8(longValues[i]);
683                    break;
684                case MTP_TYPE_UINT8:
685                    packet.putUInt8(longValues[i]);
686                    break;
687                case MTP_TYPE_INT16:
688                    packet.putInt16(longValues[i]);
689                    break;
690                case MTP_TYPE_UINT16:
691                    packet.putUInt16(longValues[i]);
692                    break;
693                case MTP_TYPE_INT32:
694                    packet.putInt32(longValues[i]);
695                    break;
696                case MTP_TYPE_UINT32:
697                    packet.putUInt32(longValues[i]);
698                    break;
699                case MTP_TYPE_INT64:
700                    packet.putInt64(longValues[i]);
701                    break;
702                case MTP_TYPE_UINT64:
703                    packet.putUInt64(longValues[i]);
704                    break;
705                case MTP_TYPE_INT128:
706                    packet.putInt128(longValues[i]);
707                    break;
708                case MTP_TYPE_UINT128:
709                    packet.putUInt128(longValues[i]);
710                    break;
711                case MTP_TYPE_STR: {
712                    jstring value = (jstring)env->GetObjectArrayElement(stringValuesArray, i);
713                    const char *valueStr = (value ? env->GetStringUTFChars(value, NULL) : NULL);
714                    if (valueStr) {
715                        packet.putString(valueStr);
716                        env->ReleaseStringUTFChars(value, valueStr);
717                    } else {
718                        packet.putEmptyString();
719                    }
720                    env->DeleteLocalRef(value);
721                    break;
722                }
723                default:
724                    LOGE("bad or unsupported data type in MyMtpDatabase::getObjectPropertyList");
725                    break;
726            }
727        }
728
729        env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0);
730        env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0);
731        env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0);
732        if (longValues)
733            env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
734
735        env->DeleteLocalRef(objectHandlesArray);
736        env->DeleteLocalRef(propertyCodesArray);
737        env->DeleteLocalRef(dataTypesArray);
738        if (longValuesArray)
739            env->DeleteLocalRef(longValuesArray);
740        if (stringValuesArray)
741            env->DeleteLocalRef(stringValuesArray);
742    }
743
744    env->DeleteLocalRef(list);
745    checkAndClearExceptionFromCallback(env, __FUNCTION__);
746    return result;
747}
748
749MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
750                                            MtpDataPacket& packet) {
751    char    date[20];
752
753    JNIEnv* env = AndroidRuntime::getJNIEnv();
754    jboolean result = env->CallBooleanMethod(mDatabase, method_getObjectInfo,
755                (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer);
756    if (!result)
757        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
758
759    jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
760    MtpStorageID storageID = intValues[0];
761    MtpObjectFormat format = intValues[1];
762    MtpObjectHandle parent = intValues[2];
763    env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
764
765    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
766    uint64_t size = longValues[0];
767    uint64_t modified = longValues[1];
768    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
769
770//    int associationType = (format == MTP_FORMAT_ASSOCIATION ?
771//                            MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
772//                            MTP_ASSOCIATION_TYPE_UNDEFINED);
773    int associationType = MTP_ASSOCIATION_TYPE_UNDEFINED;
774
775    packet.putUInt32(storageID);
776    packet.putUInt16(format);
777    packet.putUInt16(0);   // protection status
778    packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size));
779    packet.putUInt16(0);   // thumb format
780    packet.putUInt32(0);   // thumb compressed size
781    packet.putUInt32(0);   // thumb pix width
782    packet.putUInt32(0);   // thumb pix height
783    packet.putUInt32(0);   // image pix width
784    packet.putUInt32(0);   // image pix height
785    packet.putUInt32(0);   // image bit depth
786    packet.putUInt32(parent);
787    packet.putUInt16(associationType);
788    packet.putUInt32(0);   // association desc
789    packet.putUInt32(0);   // sequence number
790
791    jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
792    packet.putString(str);   // file name
793    env->ReleaseCharArrayElements(mStringBuffer, str, 0);
794
795    packet.putEmptyString();
796    formatDateTime(modified, date, sizeof(date));
797    packet.putString(date);   // date modified
798    packet.putEmptyString();   // keywords
799
800    checkAndClearExceptionFromCallback(env, __FUNCTION__);
801    return MTP_RESPONSE_OK;
802}
803
804MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle,
805                                            MtpString& outFilePath,
806                                            int64_t& outFileLength,
807                                            MtpObjectFormat& outFormat) {
808    JNIEnv* env = AndroidRuntime::getJNIEnv();
809    jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath,
810                (jint)handle, mStringBuffer, mLongBuffer);
811    if (result != MTP_RESPONSE_OK) {
812        checkAndClearExceptionFromCallback(env, __FUNCTION__);
813        return result;
814    }
815
816    jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
817    outFilePath.setTo(str, strlen16(str));
818    env->ReleaseCharArrayElements(mStringBuffer, str, 0);
819
820    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
821    outFileLength = longValues[0];
822    outFormat = longValues[1];
823    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
824
825    checkAndClearExceptionFromCallback(env, __FUNCTION__);
826    return result;
827}
828
829MtpResponseCode MyMtpDatabase::deleteFile(MtpObjectHandle handle) {
830    JNIEnv* env = AndroidRuntime::getJNIEnv();
831    MtpResponseCode result = env->CallIntMethod(mDatabase, method_deleteFile, (jint)handle);
832
833    checkAndClearExceptionFromCallback(env, __FUNCTION__);
834    return result;
835}
836
837struct PropertyTableEntry {
838    MtpObjectProperty   property;
839    int                 type;
840};
841
842static const PropertyTableEntry   kObjectPropertyTable[] = {
843    {   MTP_PROPERTY_STORAGE_ID,        MTP_TYPE_UINT32     },
844    {   MTP_PROPERTY_OBJECT_FORMAT,     MTP_TYPE_UINT16     },
845    {   MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16     },
846    {   MTP_PROPERTY_OBJECT_SIZE,       MTP_TYPE_UINT64     },
847    {   MTP_PROPERTY_OBJECT_FILE_NAME,  MTP_TYPE_STR        },
848    {   MTP_PROPERTY_DATE_MODIFIED,     MTP_TYPE_STR        },
849    {   MTP_PROPERTY_PARENT_OBJECT,     MTP_TYPE_UINT32     },
850    {   MTP_PROPERTY_PERSISTENT_UID,    MTP_TYPE_UINT128    },
851    {   MTP_PROPERTY_NAME,              MTP_TYPE_STR        },
852    {   MTP_PROPERTY_DISPLAY_NAME,      MTP_TYPE_STR        },
853    {   MTP_PROPERTY_DATE_ADDED,        MTP_TYPE_STR        },
854    {   MTP_PROPERTY_ARTIST,            MTP_TYPE_STR        },
855    {   MTP_PROPERTY_ALBUM_NAME,        MTP_TYPE_STR        },
856    {   MTP_PROPERTY_ALBUM_ARTIST,      MTP_TYPE_STR        },
857    {   MTP_PROPERTY_TRACK,             MTP_TYPE_UINT16     },
858    {   MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_TYPE_STR    },
859    {   MTP_PROPERTY_GENRE,             MTP_TYPE_STR        },
860    {   MTP_PROPERTY_COMPOSER,          MTP_TYPE_STR        },
861    {   MTP_PROPERTY_DURATION,          MTP_TYPE_UINT32     },
862    {   MTP_PROPERTY_DESCRIPTION,       MTP_TYPE_STR        },
863};
864
865static const PropertyTableEntry   kDevicePropertyTable[] = {
866    {   MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,    MTP_TYPE_STR },
867    {   MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,       MTP_TYPE_STR },
868    {   MTP_DEVICE_PROPERTY_IMAGE_SIZE,                 MTP_TYPE_STR },
869};
870
871bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
872    int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
873    const PropertyTableEntry* entry = kObjectPropertyTable;
874    for (int i = 0; i < count; i++, entry++) {
875        if (entry->property == property) {
876            type = entry->type;
877            return true;
878        }
879    }
880    return false;
881}
882
883bool MyMtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
884    int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]);
885    const PropertyTableEntry* entry = kDevicePropertyTable;
886    for (int i = 0; i < count; i++, entry++) {
887        if (entry->property == property) {
888            type = entry->type;
889            return true;
890        }
891    }
892    return false;
893}
894
895MtpObjectHandleList* MyMtpDatabase::getObjectReferences(MtpObjectHandle handle) {
896    JNIEnv* env = AndroidRuntime::getJNIEnv();
897    jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectReferences,
898                (jint)handle);
899    if (!array)
900        return NULL;
901    MtpObjectHandleList* list = new MtpObjectHandleList();
902    jint* handles = env->GetIntArrayElements(array, 0);
903    jsize length = env->GetArrayLength(array);
904    for (int i = 0; i < length; i++)
905        list->push(handles[i]);
906    env->ReleaseIntArrayElements(array, handles, 0);
907    env->DeleteLocalRef(array);
908
909    checkAndClearExceptionFromCallback(env, __FUNCTION__);
910    return list;
911}
912
913MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle,
914                                                    MtpObjectHandleList* references) {
915    JNIEnv* env = AndroidRuntime::getJNIEnv();
916    int count = references->size();
917    jintArray array = env->NewIntArray(count);
918    if (!array) {
919        LOGE("out of memory in setObjectReferences");
920        return false;
921    }
922    jint* handles = env->GetIntArrayElements(array, 0);
923     for (int i = 0; i < count; i++)
924        handles[i] = (*references)[i];
925    env->ReleaseIntArrayElements(array, handles, 0);
926    MtpResponseCode result = env->CallIntMethod(mDatabase, method_setObjectReferences,
927                (jint)handle, array);
928    env->DeleteLocalRef(array);
929
930    checkAndClearExceptionFromCallback(env, __FUNCTION__);
931    return result;
932}
933
934MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
935                                            MtpObjectFormat format) {
936    MtpProperty* result = NULL;
937    switch (property) {
938        case MTP_PROPERTY_OBJECT_FORMAT:
939            // use format as default value
940            result = new MtpProperty(property, MTP_TYPE_UINT16, false, format);
941            break;
942        case MTP_PROPERTY_PROTECTION_STATUS:
943        case MTP_PROPERTY_TRACK:
944            result = new MtpProperty(property, MTP_TYPE_UINT16);
945            break;
946        case MTP_PROPERTY_STORAGE_ID:
947        case MTP_PROPERTY_PARENT_OBJECT:
948        case MTP_PROPERTY_DURATION:
949            result = new MtpProperty(property, MTP_TYPE_UINT32);
950            break;
951        case MTP_PROPERTY_OBJECT_SIZE:
952            result = new MtpProperty(property, MTP_TYPE_UINT64);
953            break;
954        case MTP_PROPERTY_PERSISTENT_UID:
955            result = new MtpProperty(property, MTP_TYPE_UINT128);
956            break;
957        case MTP_PROPERTY_NAME:
958        case MTP_PROPERTY_DISPLAY_NAME:
959        case MTP_PROPERTY_ARTIST:
960        case MTP_PROPERTY_ALBUM_NAME:
961        case MTP_PROPERTY_ALBUM_ARTIST:
962        case MTP_PROPERTY_GENRE:
963        case MTP_PROPERTY_COMPOSER:
964        case MTP_PROPERTY_DESCRIPTION:
965            result = new MtpProperty(property, MTP_TYPE_STR);
966            break;
967        case MTP_PROPERTY_DATE_MODIFIED:
968        case MTP_PROPERTY_DATE_ADDED:
969        case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
970            result = new MtpProperty(property, MTP_TYPE_STR);
971            result->setFormDateTime();
972            break;
973        case MTP_PROPERTY_OBJECT_FILE_NAME:
974            // We allow renaming files and folders
975            result = new MtpProperty(property, MTP_TYPE_STR, true);
976            break;
977    }
978
979    return result;
980}
981
982MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
983    JNIEnv* env = AndroidRuntime::getJNIEnv();
984    MtpProperty* result = NULL;
985    bool writable = false;
986
987    switch (property) {
988        case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
989        case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
990            writable = true;
991            // fall through
992        case MTP_DEVICE_PROPERTY_IMAGE_SIZE:
993            result = new MtpProperty(property, MTP_TYPE_STR, writable);
994
995            // get current value
996            jint ret = env->CallIntMethod(mDatabase, method_getDeviceProperty,
997                        (jint)property, mLongBuffer, mStringBuffer);
998            if (ret == MTP_RESPONSE_OK) {
999                jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
1000                result->setCurrentValue(str);
1001                // for read-only properties it is safe to assume current value is default value
1002                if (!writable)
1003                    result->setDefaultValue(str);
1004                env->ReleaseCharArrayElements(mStringBuffer, str, 0);
1005            } else {
1006                LOGE("unable to read device property, response: %04X", ret);
1007            }
1008            break;
1009    }
1010
1011    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1012    return result;
1013}
1014
1015void MyMtpDatabase::sessionStarted() {
1016    JNIEnv* env = AndroidRuntime::getJNIEnv();
1017    env->CallVoidMethod(mDatabase, method_sessionStarted);
1018    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1019}
1020
1021void MyMtpDatabase::sessionEnded() {
1022    JNIEnv* env = AndroidRuntime::getJNIEnv();
1023    env->CallVoidMethod(mDatabase, method_sessionEnded);
1024    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1025}
1026
1027#endif // HAVE_ANDROID_OS
1028
1029// ----------------------------------------------------------------------------
1030
1031static void
1032android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz)
1033{
1034#ifdef HAVE_ANDROID_OS
1035    LOGD("setup\n");
1036    MyMtpDatabase* database = new MyMtpDatabase(env, thiz);
1037    env->SetIntField(thiz, field_context, (int)database);
1038    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1039#endif
1040}
1041
1042static void
1043android_mtp_MtpDatabase_finalize(JNIEnv *env, jobject thiz)
1044{
1045#ifdef HAVE_ANDROID_OS
1046    LOGD("finalize\n");
1047    MyMtpDatabase* database = (MyMtpDatabase *)env->GetIntField(thiz, field_context);
1048    database->cleanup(env);
1049    delete database;
1050    env->SetIntField(thiz, field_context, 0);
1051    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1052#endif
1053}
1054
1055static jstring
1056android_mtp_MtpPropertyGroup_format_date_time(JNIEnv *env, jobject thiz, jlong seconds)
1057{
1058#ifdef HAVE_ANDROID_OS
1059    char    date[20];
1060    formatDateTime(seconds, date, sizeof(date));
1061    return env->NewStringUTF(date);
1062#else
1063    return NULL;
1064#endif
1065}
1066
1067// ----------------------------------------------------------------------------
1068
1069static JNINativeMethod gMtpDatabaseMethods[] = {
1070    {"native_setup",            "()V",  (void *)android_mtp_MtpDatabase_setup},
1071    {"native_finalize",         "()V",  (void *)android_mtp_MtpDatabase_finalize},
1072};
1073
1074static JNINativeMethod gMtpPropertyGroupMethods[] = {
1075    {"format_date_time",        "(J)Ljava/lang/String;",
1076                                        (void *)android_mtp_MtpPropertyGroup_format_date_time},
1077};
1078
1079static const char* const kClassPathName = "android/mtp/MtpDatabase";
1080
1081int register_android_mtp_MtpDatabase(JNIEnv *env)
1082{
1083    jclass clazz;
1084
1085    LOGD("register_android_mtp_MtpDatabase\n");
1086
1087    clazz = env->FindClass("android/mtp/MtpDatabase");
1088    if (clazz == NULL) {
1089        LOGE("Can't find android/mtp/MtpDatabase");
1090        return -1;
1091    }
1092    method_beginSendObject = env->GetMethodID(clazz, "beginSendObject", "(Ljava/lang/String;IIIJJ)I");
1093    if (method_beginSendObject == NULL) {
1094        LOGE("Can't find beginSendObject");
1095        return -1;
1096    }
1097    method_endSendObject = env->GetMethodID(clazz, "endSendObject", "(Ljava/lang/String;IIJZ)V");
1098    if (method_endSendObject == NULL) {
1099        LOGE("Can't find endSendObject");
1100        return -1;
1101    }
1102    method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I");
1103    if (method_getObjectList == NULL) {
1104        LOGE("Can't find getObjectList");
1105        return -1;
1106    }
1107    method_getNumObjects = env->GetMethodID(clazz, "getNumObjects", "(III)I");
1108    if (method_getNumObjects == NULL) {
1109        LOGE("Can't find getNumObjects");
1110        return -1;
1111    }
1112    method_getSupportedPlaybackFormats = env->GetMethodID(clazz, "getSupportedPlaybackFormats", "()[I");
1113    if (method_getSupportedPlaybackFormats == NULL) {
1114        LOGE("Can't find getSupportedPlaybackFormats");
1115        return -1;
1116    }
1117    method_getSupportedCaptureFormats = env->GetMethodID(clazz, "getSupportedCaptureFormats", "()[I");
1118    if (method_getSupportedCaptureFormats == NULL) {
1119        LOGE("Can't find getSupportedCaptureFormats");
1120        return -1;
1121    }
1122    method_getSupportedObjectProperties = env->GetMethodID(clazz, "getSupportedObjectProperties", "(I)[I");
1123    if (method_getSupportedObjectProperties == NULL) {
1124        LOGE("Can't find getSupportedObjectProperties");
1125        return -1;
1126    }
1127    method_getSupportedDeviceProperties = env->GetMethodID(clazz, "getSupportedDeviceProperties", "()[I");
1128    if (method_getSupportedDeviceProperties == NULL) {
1129        LOGE("Can't find getSupportedDeviceProperties");
1130        return -1;
1131    }
1132    method_setObjectProperty = env->GetMethodID(clazz, "setObjectProperty", "(IIJLjava/lang/String;)I");
1133    if (method_setObjectProperty == NULL) {
1134        LOGE("Can't find setObjectProperty");
1135        return -1;
1136    }
1137    method_getDeviceProperty = env->GetMethodID(clazz, "getDeviceProperty", "(I[J[C)I");
1138    if (method_getDeviceProperty == NULL) {
1139        LOGE("Can't find getDeviceProperty");
1140        return -1;
1141    }
1142    method_setDeviceProperty = env->GetMethodID(clazz, "setDeviceProperty", "(IJLjava/lang/String;)I");
1143    if (method_setDeviceProperty == NULL) {
1144        LOGE("Can't find setDeviceProperty");
1145        return -1;
1146    }
1147    method_getObjectPropertyList = env->GetMethodID(clazz, "getObjectPropertyList",
1148            "(JIJII)Landroid/mtp/MtpPropertyList;");
1149    if (method_getObjectPropertyList == NULL) {
1150        LOGE("Can't find getObjectPropertyList");
1151        return -1;
1152    }
1153    method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z");
1154    if (method_getObjectInfo == NULL) {
1155        LOGE("Can't find getObjectInfo");
1156        return -1;
1157    }
1158    method_getObjectFilePath = env->GetMethodID(clazz, "getObjectFilePath", "(I[C[J)I");
1159    if (method_getObjectFilePath == NULL) {
1160        LOGE("Can't find getObjectFilePath");
1161        return -1;
1162    }
1163    method_deleteFile = env->GetMethodID(clazz, "deleteFile", "(I)I");
1164    if (method_deleteFile == NULL) {
1165        LOGE("Can't find deleteFile");
1166        return -1;
1167    }
1168    method_getObjectReferences = env->GetMethodID(clazz, "getObjectReferences", "(I)[I");
1169    if (method_getObjectReferences == NULL) {
1170        LOGE("Can't find getObjectReferences");
1171        return -1;
1172    }
1173    method_setObjectReferences = env->GetMethodID(clazz, "setObjectReferences", "(I[I)I");
1174    if (method_setObjectReferences == NULL) {
1175        LOGE("Can't find setObjectReferences");
1176        return -1;
1177    }
1178    method_sessionStarted = env->GetMethodID(clazz, "sessionStarted", "()V");
1179    if (method_sessionStarted == NULL) {
1180        LOGE("Can't find sessionStarted");
1181        return -1;
1182    }
1183    method_sessionEnded = env->GetMethodID(clazz, "sessionEnded", "()V");
1184    if (method_sessionEnded == NULL) {
1185        LOGE("Can't find sessionEnded");
1186        return -1;
1187    }
1188
1189    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
1190    if (field_context == NULL) {
1191        LOGE("Can't find MtpDatabase.mNativeContext");
1192        return -1;
1193    }
1194
1195    // now set up fields for MtpPropertyList class
1196    clazz = env->FindClass("android/mtp/MtpPropertyList");
1197    if (clazz == NULL) {
1198        LOGE("Can't find android/mtp/MtpPropertyList");
1199        return -1;
1200    }
1201    field_mCount = env->GetFieldID(clazz, "mCount", "I");
1202    if (field_mCount == NULL) {
1203        LOGE("Can't find MtpPropertyList.mCount");
1204        return -1;
1205    }
1206    field_mResult = env->GetFieldID(clazz, "mResult", "I");
1207    if (field_mResult == NULL) {
1208        LOGE("Can't find MtpPropertyList.mResult");
1209        return -1;
1210    }
1211    field_mObjectHandles = env->GetFieldID(clazz, "mObjectHandles", "[I");
1212    if (field_mObjectHandles == NULL) {
1213        LOGE("Can't find MtpPropertyList.mObjectHandles");
1214        return -1;
1215    }
1216    field_mPropertyCodes = env->GetFieldID(clazz, "mPropertyCodes", "[I");
1217    if (field_mPropertyCodes == NULL) {
1218        LOGE("Can't find MtpPropertyList.mPropertyCodes");
1219        return -1;
1220    }
1221    field_mDataTypes = env->GetFieldID(clazz, "mDataTypes", "[I");
1222    if (field_mDataTypes == NULL) {
1223        LOGE("Can't find MtpPropertyList.mDataTypes");
1224        return -1;
1225    }
1226    field_mLongValues = env->GetFieldID(clazz, "mLongValues", "[J");
1227    if (field_mLongValues == NULL) {
1228        LOGE("Can't find MtpPropertyList.mLongValues");
1229        return -1;
1230    }
1231    field_mStringValues = env->GetFieldID(clazz, "mStringValues", "[Ljava/lang/String;");
1232    if (field_mStringValues == NULL) {
1233        LOGE("Can't find MtpPropertyList.mStringValues");
1234        return -1;
1235    }
1236
1237    if (AndroidRuntime::registerNativeMethods(env,
1238                "android/mtp/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods)))
1239        return -1;
1240
1241    return AndroidRuntime::registerNativeMethods(env,
1242                "android/mtp/MtpPropertyGroup", gMtpPropertyGroupMethods, NELEM(gMtpPropertyGroupMethods));
1243}
1244