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