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