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