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