android_mtp_MtpDatabase.cpp revision 959bce3e69b4eb5dcba9ec48d92f21002f9c13b3
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                if (stringValue) {
435                    const char* str = env->GetStringUTFChars(stringValue, NULL);
436                    if (str == NULL) {
437                        return MTP_RESPONSE_GENERAL_ERROR;
438                    }
439                    packet.putString(str);
440                    env->ReleaseStringUTFChars(stringValue, str);
441                } else {
442                    packet.putEmptyString();
443                }
444                break;
445             }
446            default:
447                ALOGE("unsupported type in getObjectPropertyValue\n");
448                result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
449        }
450out:
451        env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0);
452        env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0);
453        env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0);
454        if (longValues)
455            env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
456
457        env->DeleteLocalRef(objectHandlesArray);
458        env->DeleteLocalRef(propertyCodesArray);
459        env->DeleteLocalRef(dataTypesArray);
460        if (longValuesArray)
461            env->DeleteLocalRef(longValuesArray);
462        if (stringValuesArray)
463            env->DeleteLocalRef(stringValuesArray);
464    }
465
466    env->DeleteLocalRef(list);
467    checkAndClearExceptionFromCallback(env, __FUNCTION__);
468    return result;
469}
470
471MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
472                                            MtpObjectProperty property,
473                                            MtpDataPacket& packet) {
474    int         type;
475
476    if (!getObjectPropertyInfo(property, type))
477        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
478
479    JNIEnv* env = AndroidRuntime::getJNIEnv();
480    jlong longValue = 0;
481    jstring stringValue = NULL;
482
483    switch (type) {
484        case MTP_TYPE_INT8:
485            longValue = packet.getInt8();
486            break;
487        case MTP_TYPE_UINT8:
488            longValue = packet.getUInt8();
489            break;
490        case MTP_TYPE_INT16:
491            longValue = packet.getInt16();
492            break;
493        case MTP_TYPE_UINT16:
494            longValue = packet.getUInt16();
495            break;
496        case MTP_TYPE_INT32:
497            longValue = packet.getInt32();
498            break;
499        case MTP_TYPE_UINT32:
500            longValue = packet.getUInt32();
501            break;
502        case MTP_TYPE_INT64:
503            longValue = packet.getInt64();
504            break;
505        case MTP_TYPE_UINT64:
506            longValue = packet.getUInt64();
507            break;
508        case MTP_TYPE_STR:
509        {
510            MtpStringBuffer buffer;
511            packet.getString(buffer);
512            stringValue = env->NewStringUTF((const char *)buffer);
513            break;
514         }
515        default:
516            ALOGE("unsupported type in getObjectPropertyValue\n");
517            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
518    }
519
520    jint result = env->CallIntMethod(mDatabase, method_setObjectProperty,
521                (jint)handle, (jint)property, longValue, stringValue);
522    if (stringValue)
523        env->DeleteLocalRef(stringValue);
524
525    checkAndClearExceptionFromCallback(env, __FUNCTION__);
526    return result;
527}
528
529MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
530                                            MtpDataPacket& packet) {
531    int         type;
532
533    if (!getDevicePropertyInfo(property, type))
534        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
535
536    JNIEnv* env = AndroidRuntime::getJNIEnv();
537    jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty,
538                (jint)property, mLongBuffer, mStringBuffer);
539    if (result != MTP_RESPONSE_OK) {
540        checkAndClearExceptionFromCallback(env, __FUNCTION__);
541        return result;
542    }
543
544    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
545    jlong longValue = longValues[0];
546    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
547
548    switch (type) {
549        case MTP_TYPE_INT8:
550            packet.putInt8(longValue);
551            break;
552        case MTP_TYPE_UINT8:
553            packet.putUInt8(longValue);
554            break;
555        case MTP_TYPE_INT16:
556            packet.putInt16(longValue);
557            break;
558        case MTP_TYPE_UINT16:
559            packet.putUInt16(longValue);
560            break;
561        case MTP_TYPE_INT32:
562            packet.putInt32(longValue);
563            break;
564        case MTP_TYPE_UINT32:
565            packet.putUInt32(longValue);
566            break;
567        case MTP_TYPE_INT64:
568            packet.putInt64(longValue);
569            break;
570        case MTP_TYPE_UINT64:
571            packet.putUInt64(longValue);
572            break;
573        case MTP_TYPE_INT128:
574            packet.putInt128(longValue);
575            break;
576        case MTP_TYPE_UINT128:
577            packet.putInt128(longValue);
578            break;
579        case MTP_TYPE_STR:
580        {
581            jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
582            packet.putString(str);
583            env->ReleaseCharArrayElements(mStringBuffer, str, 0);
584            break;
585         }
586        default:
587            ALOGE("unsupported type in getDevicePropertyValue\n");
588            return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
589    }
590
591    checkAndClearExceptionFromCallback(env, __FUNCTION__);
592    return MTP_RESPONSE_OK;
593}
594
595MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property,
596                                            MtpDataPacket& packet) {
597    int         type;
598
599    if (!getDevicePropertyInfo(property, type))
600        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
601
602    JNIEnv* env = AndroidRuntime::getJNIEnv();
603    jlong longValue = 0;
604    jstring stringValue = NULL;
605
606    switch (type) {
607        case MTP_TYPE_INT8:
608            longValue = packet.getInt8();
609            break;
610        case MTP_TYPE_UINT8:
611            longValue = packet.getUInt8();
612            break;
613        case MTP_TYPE_INT16:
614            longValue = packet.getInt16();
615            break;
616        case MTP_TYPE_UINT16:
617            longValue = packet.getUInt16();
618            break;
619        case MTP_TYPE_INT32:
620            longValue = packet.getInt32();
621            break;
622        case MTP_TYPE_UINT32:
623            longValue = packet.getUInt32();
624            break;
625        case MTP_TYPE_INT64:
626            longValue = packet.getInt64();
627            break;
628        case MTP_TYPE_UINT64:
629            longValue = packet.getUInt64();
630            break;
631        case MTP_TYPE_STR:
632        {
633            MtpStringBuffer buffer;
634            packet.getString(buffer);
635            stringValue = env->NewStringUTF((const char *)buffer);
636            break;
637         }
638        default:
639            ALOGE("unsupported type in setDevicePropertyValue\n");
640            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
641    }
642
643    jint result = env->CallIntMethod(mDatabase, method_setDeviceProperty,
644                (jint)property, longValue, stringValue);
645    if (stringValue)
646        env->DeleteLocalRef(stringValue);
647
648    checkAndClearExceptionFromCallback(env, __FUNCTION__);
649    return result;
650}
651
652MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty /*property*/) {
653    return -1;
654}
655
656MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle,
657                                            uint32_t format, uint32_t property,
658                                            int groupCode, int depth,
659                                            MtpDataPacket& packet) {
660    JNIEnv* env = AndroidRuntime::getJNIEnv();
661    jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList,
662                (jlong)handle, (jint)format, (jlong)property, (jint)groupCode, (jint)depth);
663    checkAndClearExceptionFromCallback(env, __FUNCTION__);
664    if (!list)
665        return MTP_RESPONSE_GENERAL_ERROR;
666    int count = env->GetIntField(list, field_mCount);
667    MtpResponseCode result = env->GetIntField(list, field_mResult);
668
669    packet.putUInt32(count);
670    if (count > 0) {
671        jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles);
672        jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes);
673        jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes);
674        jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues);
675        jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues);
676
677        jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0);
678        jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0);
679        jint* dataTypes = env->GetIntArrayElements(dataTypesArray, 0);
680        jlong* longValues = (longValuesArray ? env->GetLongArrayElements(longValuesArray, 0) : NULL);
681
682        for (int i = 0; i < count; i++) {
683            packet.putUInt32(objectHandles[i]);
684            packet.putUInt16(propertyCodes[i]);
685            int type = dataTypes[i];
686            packet.putUInt16(type);
687
688            switch (type) {
689                case MTP_TYPE_INT8:
690                    packet.putInt8(longValues[i]);
691                    break;
692                case MTP_TYPE_UINT8:
693                    packet.putUInt8(longValues[i]);
694                    break;
695                case MTP_TYPE_INT16:
696                    packet.putInt16(longValues[i]);
697                    break;
698                case MTP_TYPE_UINT16:
699                    packet.putUInt16(longValues[i]);
700                    break;
701                case MTP_TYPE_INT32:
702                    packet.putInt32(longValues[i]);
703                    break;
704                case MTP_TYPE_UINT32:
705                    packet.putUInt32(longValues[i]);
706                    break;
707                case MTP_TYPE_INT64:
708                    packet.putInt64(longValues[i]);
709                    break;
710                case MTP_TYPE_UINT64:
711                    packet.putUInt64(longValues[i]);
712                    break;
713                case MTP_TYPE_INT128:
714                    packet.putInt128(longValues[i]);
715                    break;
716                case MTP_TYPE_UINT128:
717                    packet.putUInt128(longValues[i]);
718                    break;
719                case MTP_TYPE_STR: {
720                    jstring value = (jstring)env->GetObjectArrayElement(stringValuesArray, i);
721                    const char *valueStr = (value ? env->GetStringUTFChars(value, NULL) : NULL);
722                    if (valueStr) {
723                        packet.putString(valueStr);
724                        env->ReleaseStringUTFChars(value, valueStr);
725                    } else {
726                        packet.putEmptyString();
727                    }
728                    env->DeleteLocalRef(value);
729                    break;
730                }
731                default:
732                    ALOGE("bad or unsupported data type in MyMtpDatabase::getObjectPropertyList");
733                    break;
734            }
735        }
736
737        env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0);
738        env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0);
739        env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0);
740        if (longValues)
741            env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
742
743        env->DeleteLocalRef(objectHandlesArray);
744        env->DeleteLocalRef(propertyCodesArray);
745        env->DeleteLocalRef(dataTypesArray);
746        if (longValuesArray)
747            env->DeleteLocalRef(longValuesArray);
748        if (stringValuesArray)
749            env->DeleteLocalRef(stringValuesArray);
750    }
751
752    env->DeleteLocalRef(list);
753    checkAndClearExceptionFromCallback(env, __FUNCTION__);
754    return result;
755}
756
757static void foreachentry(ExifEntry *entry, void *user) {
758    char buf[1024];
759    ALOGI("entry %x, format %d, size %d: %s",
760            entry->tag, entry->format, entry->size, exif_entry_get_value(entry, buf, sizeof(buf)));
761}
762
763static void foreachcontent(ExifContent *content, void *user) {
764    ALOGI("content %d", exif_content_get_ifd(content));
765    exif_content_foreach_entry(content, foreachentry, user);
766}
767
768static long getLongFromExifEntry(ExifEntry *e) {
769    ExifByteOrder o = exif_data_get_byte_order(e->parent->parent);
770    return exif_get_long(e->data, o);
771}
772
773MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
774                                            MtpObjectInfo& info) {
775    char            date[20];
776    MtpString       path;
777    int64_t         length;
778    MtpObjectFormat format;
779
780    MtpResponseCode result = getObjectFilePath(handle, path, length, format);
781    if (result != MTP_RESPONSE_OK) {
782        return result;
783    }
784    info.mCompressedSize = (length > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)length);
785
786    JNIEnv* env = AndroidRuntime::getJNIEnv();
787    if (!env->CallBooleanMethod(mDatabase, method_getObjectInfo,
788                (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer)) {
789        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
790    }
791
792    jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
793    info.mStorageID = intValues[0];
794    info.mFormat = intValues[1];
795    info.mParent = intValues[2];
796    env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
797
798    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
799    info.mDateCreated = longValues[0];
800    info.mDateModified = longValues[1];
801    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
802
803//    info.mAssociationType = (format == MTP_FORMAT_ASSOCIATION ?
804//                            MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
805//                            MTP_ASSOCIATION_TYPE_UNDEFINED);
806    info.mAssociationType = MTP_ASSOCIATION_TYPE_UNDEFINED;
807
808    jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
809    MtpString temp(str);
810    info.mName = strdup((const char *)temp);
811    env->ReleaseCharArrayElements(mStringBuffer, str, 0);
812
813    // read EXIF data for thumbnail information
814    if (info.mFormat == MTP_FORMAT_EXIF_JPEG || info.mFormat == MTP_FORMAT_JFIF) {
815
816        ExifData *exifdata = exif_data_new_from_file(path);
817        if (exifdata) {
818            //exif_data_foreach_content(exifdata, foreachcontent, NULL);
819
820            // XXX get this from exif, or parse jpeg header instead?
821            ExifEntry *w = exif_content_get_entry(
822                    exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_X_DIMENSION);
823            ExifEntry *h = exif_content_get_entry(
824                    exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_Y_DIMENSION);
825            info.mThumbCompressedSize = exifdata->data ? exifdata->size : 0;
826            info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
827            info.mImagePixWidth = w ? getLongFromExifEntry(w) : 0;
828            info.mImagePixHeight = h ? getLongFromExifEntry(h) : 0;
829            exif_data_unref(exifdata);
830        }
831    }
832
833    checkAndClearExceptionFromCallback(env, __FUNCTION__);
834    return MTP_RESPONSE_OK;
835}
836
837void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
838    MtpString path;
839    int64_t length;
840    MtpObjectFormat format;
841    void* result = NULL;
842    outThumbSize = 0;
843
844    if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK
845            && (format == MTP_FORMAT_EXIF_JPEG || format == MTP_FORMAT_JFIF)) {
846
847        ExifData *exifdata = exif_data_new_from_file(path);
848        if (exifdata) {
849            if (exifdata->data) {
850                result = malloc(exifdata->size);
851                if (result) {
852                    memcpy(result, exifdata->data, exifdata->size);
853                }
854            }
855            exif_data_unref(exifdata);
856        }
857    }
858
859    return result;
860}
861
862MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle,
863                                            MtpString& outFilePath,
864                                            int64_t& outFileLength,
865                                            MtpObjectFormat& outFormat) {
866    JNIEnv* env = AndroidRuntime::getJNIEnv();
867    jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath,
868                (jint)handle, mStringBuffer, mLongBuffer);
869    if (result != MTP_RESPONSE_OK) {
870        checkAndClearExceptionFromCallback(env, __FUNCTION__);
871        return result;
872    }
873
874    jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
875    outFilePath.setTo(str, strlen16(str));
876    env->ReleaseCharArrayElements(mStringBuffer, str, 0);
877
878    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
879    outFileLength = longValues[0];
880    outFormat = longValues[1];
881    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
882
883    checkAndClearExceptionFromCallback(env, __FUNCTION__);
884    return result;
885}
886
887MtpResponseCode MyMtpDatabase::deleteFile(MtpObjectHandle handle) {
888    JNIEnv* env = AndroidRuntime::getJNIEnv();
889    MtpResponseCode result = env->CallIntMethod(mDatabase, method_deleteFile, (jint)handle);
890
891    checkAndClearExceptionFromCallback(env, __FUNCTION__);
892    return result;
893}
894
895struct PropertyTableEntry {
896    MtpObjectProperty   property;
897    int                 type;
898};
899
900static const PropertyTableEntry   kObjectPropertyTable[] = {
901    {   MTP_PROPERTY_STORAGE_ID,        MTP_TYPE_UINT32     },
902    {   MTP_PROPERTY_OBJECT_FORMAT,     MTP_TYPE_UINT16     },
903    {   MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16     },
904    {   MTP_PROPERTY_OBJECT_SIZE,       MTP_TYPE_UINT64     },
905    {   MTP_PROPERTY_OBJECT_FILE_NAME,  MTP_TYPE_STR        },
906    {   MTP_PROPERTY_DATE_MODIFIED,     MTP_TYPE_STR        },
907    {   MTP_PROPERTY_PARENT_OBJECT,     MTP_TYPE_UINT32     },
908    {   MTP_PROPERTY_PERSISTENT_UID,    MTP_TYPE_UINT128    },
909    {   MTP_PROPERTY_NAME,              MTP_TYPE_STR        },
910    {   MTP_PROPERTY_DISPLAY_NAME,      MTP_TYPE_STR        },
911    {   MTP_PROPERTY_DATE_ADDED,        MTP_TYPE_STR        },
912    {   MTP_PROPERTY_ARTIST,            MTP_TYPE_STR        },
913    {   MTP_PROPERTY_ALBUM_NAME,        MTP_TYPE_STR        },
914    {   MTP_PROPERTY_ALBUM_ARTIST,      MTP_TYPE_STR        },
915    {   MTP_PROPERTY_TRACK,             MTP_TYPE_UINT16     },
916    {   MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_TYPE_STR    },
917    {   MTP_PROPERTY_GENRE,             MTP_TYPE_STR        },
918    {   MTP_PROPERTY_COMPOSER,          MTP_TYPE_STR        },
919    {   MTP_PROPERTY_DURATION,          MTP_TYPE_UINT32     },
920    {   MTP_PROPERTY_DESCRIPTION,       MTP_TYPE_STR        },
921};
922
923static const PropertyTableEntry   kDevicePropertyTable[] = {
924    {   MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,    MTP_TYPE_STR },
925    {   MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,       MTP_TYPE_STR },
926    {   MTP_DEVICE_PROPERTY_IMAGE_SIZE,                 MTP_TYPE_STR },
927};
928
929bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
930    int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
931    const PropertyTableEntry* entry = kObjectPropertyTable;
932    for (int i = 0; i < count; i++, entry++) {
933        if (entry->property == property) {
934            type = entry->type;
935            return true;
936        }
937    }
938    return false;
939}
940
941bool MyMtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
942    int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]);
943    const PropertyTableEntry* entry = kDevicePropertyTable;
944    for (int i = 0; i < count; i++, entry++) {
945        if (entry->property == property) {
946            type = entry->type;
947            return true;
948        }
949    }
950    return false;
951}
952
953MtpObjectHandleList* MyMtpDatabase::getObjectReferences(MtpObjectHandle handle) {
954    JNIEnv* env = AndroidRuntime::getJNIEnv();
955    jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectReferences,
956                (jint)handle);
957    if (!array)
958        return NULL;
959    MtpObjectHandleList* list = new MtpObjectHandleList();
960    jint* handles = env->GetIntArrayElements(array, 0);
961    jsize length = env->GetArrayLength(array);
962    for (int i = 0; i < length; i++)
963        list->push(handles[i]);
964    env->ReleaseIntArrayElements(array, handles, 0);
965    env->DeleteLocalRef(array);
966
967    checkAndClearExceptionFromCallback(env, __FUNCTION__);
968    return list;
969}
970
971MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle,
972                                                    MtpObjectHandleList* references) {
973    JNIEnv* env = AndroidRuntime::getJNIEnv();
974    int count = references->size();
975    jintArray array = env->NewIntArray(count);
976    if (!array) {
977        ALOGE("out of memory in setObjectReferences");
978        return false;
979    }
980    jint* handles = env->GetIntArrayElements(array, 0);
981     for (int i = 0; i < count; i++)
982        handles[i] = (*references)[i];
983    env->ReleaseIntArrayElements(array, handles, 0);
984    MtpResponseCode result = env->CallIntMethod(mDatabase, method_setObjectReferences,
985                (jint)handle, array);
986    env->DeleteLocalRef(array);
987
988    checkAndClearExceptionFromCallback(env, __FUNCTION__);
989    return result;
990}
991
992MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
993                                            MtpObjectFormat format) {
994    MtpProperty* result = NULL;
995    switch (property) {
996        case MTP_PROPERTY_OBJECT_FORMAT:
997            // use format as default value
998            result = new MtpProperty(property, MTP_TYPE_UINT16, false, format);
999            break;
1000        case MTP_PROPERTY_PROTECTION_STATUS:
1001        case MTP_PROPERTY_TRACK:
1002            result = new MtpProperty(property, MTP_TYPE_UINT16);
1003            break;
1004        case MTP_PROPERTY_STORAGE_ID:
1005        case MTP_PROPERTY_PARENT_OBJECT:
1006        case MTP_PROPERTY_DURATION:
1007            result = new MtpProperty(property, MTP_TYPE_UINT32);
1008            break;
1009        case MTP_PROPERTY_OBJECT_SIZE:
1010            result = new MtpProperty(property, MTP_TYPE_UINT64);
1011            break;
1012        case MTP_PROPERTY_PERSISTENT_UID:
1013            result = new MtpProperty(property, MTP_TYPE_UINT128);
1014            break;
1015        case MTP_PROPERTY_NAME:
1016        case MTP_PROPERTY_DISPLAY_NAME:
1017        case MTP_PROPERTY_ARTIST:
1018        case MTP_PROPERTY_ALBUM_NAME:
1019        case MTP_PROPERTY_ALBUM_ARTIST:
1020        case MTP_PROPERTY_GENRE:
1021        case MTP_PROPERTY_COMPOSER:
1022        case MTP_PROPERTY_DESCRIPTION:
1023            result = new MtpProperty(property, MTP_TYPE_STR);
1024            break;
1025        case MTP_PROPERTY_DATE_MODIFIED:
1026        case MTP_PROPERTY_DATE_ADDED:
1027        case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
1028            result = new MtpProperty(property, MTP_TYPE_STR);
1029            result->setFormDateTime();
1030            break;
1031        case MTP_PROPERTY_OBJECT_FILE_NAME:
1032            // We allow renaming files and folders
1033            result = new MtpProperty(property, MTP_TYPE_STR, true);
1034            break;
1035    }
1036
1037    return result;
1038}
1039
1040MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
1041    JNIEnv* env = AndroidRuntime::getJNIEnv();
1042    MtpProperty* result = NULL;
1043    bool writable = false;
1044
1045    switch (property) {
1046        case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
1047        case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
1048            writable = true;
1049            // fall through
1050        case MTP_DEVICE_PROPERTY_IMAGE_SIZE:
1051            result = new MtpProperty(property, MTP_TYPE_STR, writable);
1052
1053            // get current value
1054            jint ret = env->CallIntMethod(mDatabase, method_getDeviceProperty,
1055                        (jint)property, mLongBuffer, mStringBuffer);
1056            if (ret == MTP_RESPONSE_OK) {
1057                jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
1058                result->setCurrentValue(str);
1059                // for read-only properties it is safe to assume current value is default value
1060                if (!writable)
1061                    result->setDefaultValue(str);
1062                env->ReleaseCharArrayElements(mStringBuffer, str, 0);
1063            } else {
1064                ALOGE("unable to read device property, response: %04X", ret);
1065            }
1066            break;
1067    }
1068
1069    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1070    return result;
1071}
1072
1073void MyMtpDatabase::sessionStarted() {
1074    JNIEnv* env = AndroidRuntime::getJNIEnv();
1075    env->CallVoidMethod(mDatabase, method_sessionStarted);
1076    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1077}
1078
1079void MyMtpDatabase::sessionEnded() {
1080    JNIEnv* env = AndroidRuntime::getJNIEnv();
1081    env->CallVoidMethod(mDatabase, method_sessionEnded);
1082    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1083}
1084
1085// ----------------------------------------------------------------------------
1086
1087static void
1088android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz)
1089{
1090    MyMtpDatabase* database = new MyMtpDatabase(env, thiz);
1091    env->SetLongField(thiz, field_context, (jlong)database);
1092    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1093}
1094
1095static void
1096android_mtp_MtpDatabase_finalize(JNIEnv *env, jobject thiz)
1097{
1098    MyMtpDatabase* database = (MyMtpDatabase *)env->GetLongField(thiz, field_context);
1099    database->cleanup(env);
1100    delete database;
1101    env->SetLongField(thiz, field_context, 0);
1102    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1103}
1104
1105static jstring
1106android_mtp_MtpPropertyGroup_format_date_time(JNIEnv *env, jobject /*thiz*/, jlong seconds)
1107{
1108    char    date[20];
1109    formatDateTime(seconds, date, sizeof(date));
1110    return env->NewStringUTF(date);
1111}
1112
1113// ----------------------------------------------------------------------------
1114
1115static JNINativeMethod gMtpDatabaseMethods[] = {
1116    {"native_setup",            "()V",  (void *)android_mtp_MtpDatabase_setup},
1117    {"native_finalize",         "()V",  (void *)android_mtp_MtpDatabase_finalize},
1118};
1119
1120static JNINativeMethod gMtpPropertyGroupMethods[] = {
1121    {"format_date_time",        "(J)Ljava/lang/String;",
1122                                        (void *)android_mtp_MtpPropertyGroup_format_date_time},
1123};
1124
1125static const char* const kClassPathName = "android/mtp/MtpDatabase";
1126
1127int register_android_mtp_MtpDatabase(JNIEnv *env)
1128{
1129    jclass clazz;
1130
1131    clazz = env->FindClass("android/mtp/MtpDatabase");
1132    if (clazz == NULL) {
1133        ALOGE("Can't find android/mtp/MtpDatabase");
1134        return -1;
1135    }
1136    method_beginSendObject = env->GetMethodID(clazz, "beginSendObject", "(Ljava/lang/String;IIIJJ)I");
1137    if (method_beginSendObject == NULL) {
1138        ALOGE("Can't find beginSendObject");
1139        return -1;
1140    }
1141    method_endSendObject = env->GetMethodID(clazz, "endSendObject", "(Ljava/lang/String;IIZ)V");
1142    if (method_endSendObject == NULL) {
1143        ALOGE("Can't find endSendObject");
1144        return -1;
1145    }
1146    method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I");
1147    if (method_getObjectList == NULL) {
1148        ALOGE("Can't find getObjectList");
1149        return -1;
1150    }
1151    method_getNumObjects = env->GetMethodID(clazz, "getNumObjects", "(III)I");
1152    if (method_getNumObjects == NULL) {
1153        ALOGE("Can't find getNumObjects");
1154        return -1;
1155    }
1156    method_getSupportedPlaybackFormats = env->GetMethodID(clazz, "getSupportedPlaybackFormats", "()[I");
1157    if (method_getSupportedPlaybackFormats == NULL) {
1158        ALOGE("Can't find getSupportedPlaybackFormats");
1159        return -1;
1160    }
1161    method_getSupportedCaptureFormats = env->GetMethodID(clazz, "getSupportedCaptureFormats", "()[I");
1162    if (method_getSupportedCaptureFormats == NULL) {
1163        ALOGE("Can't find getSupportedCaptureFormats");
1164        return -1;
1165    }
1166    method_getSupportedObjectProperties = env->GetMethodID(clazz, "getSupportedObjectProperties", "(I)[I");
1167    if (method_getSupportedObjectProperties == NULL) {
1168        ALOGE("Can't find getSupportedObjectProperties");
1169        return -1;
1170    }
1171    method_getSupportedDeviceProperties = env->GetMethodID(clazz, "getSupportedDeviceProperties", "()[I");
1172    if (method_getSupportedDeviceProperties == NULL) {
1173        ALOGE("Can't find getSupportedDeviceProperties");
1174        return -1;
1175    }
1176    method_setObjectProperty = env->GetMethodID(clazz, "setObjectProperty", "(IIJLjava/lang/String;)I");
1177    if (method_setObjectProperty == NULL) {
1178        ALOGE("Can't find setObjectProperty");
1179        return -1;
1180    }
1181    method_getDeviceProperty = env->GetMethodID(clazz, "getDeviceProperty", "(I[J[C)I");
1182    if (method_getDeviceProperty == NULL) {
1183        ALOGE("Can't find getDeviceProperty");
1184        return -1;
1185    }
1186    method_setDeviceProperty = env->GetMethodID(clazz, "setDeviceProperty", "(IJLjava/lang/String;)I");
1187    if (method_setDeviceProperty == NULL) {
1188        ALOGE("Can't find setDeviceProperty");
1189        return -1;
1190    }
1191    method_getObjectPropertyList = env->GetMethodID(clazz, "getObjectPropertyList",
1192            "(JIJII)Landroid/mtp/MtpPropertyList;");
1193    if (method_getObjectPropertyList == NULL) {
1194        ALOGE("Can't find getObjectPropertyList");
1195        return -1;
1196    }
1197    method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z");
1198    if (method_getObjectInfo == NULL) {
1199        ALOGE("Can't find getObjectInfo");
1200        return -1;
1201    }
1202    method_getObjectFilePath = env->GetMethodID(clazz, "getObjectFilePath", "(I[C[J)I");
1203    if (method_getObjectFilePath == NULL) {
1204        ALOGE("Can't find getObjectFilePath");
1205        return -1;
1206    }
1207    method_deleteFile = env->GetMethodID(clazz, "deleteFile", "(I)I");
1208    if (method_deleteFile == NULL) {
1209        ALOGE("Can't find deleteFile");
1210        return -1;
1211    }
1212    method_getObjectReferences = env->GetMethodID(clazz, "getObjectReferences", "(I)[I");
1213    if (method_getObjectReferences == NULL) {
1214        ALOGE("Can't find getObjectReferences");
1215        return -1;
1216    }
1217    method_setObjectReferences = env->GetMethodID(clazz, "setObjectReferences", "(I[I)I");
1218    if (method_setObjectReferences == NULL) {
1219        ALOGE("Can't find setObjectReferences");
1220        return -1;
1221    }
1222    method_sessionStarted = env->GetMethodID(clazz, "sessionStarted", "()V");
1223    if (method_sessionStarted == NULL) {
1224        ALOGE("Can't find sessionStarted");
1225        return -1;
1226    }
1227    method_sessionEnded = env->GetMethodID(clazz, "sessionEnded", "()V");
1228    if (method_sessionEnded == NULL) {
1229        ALOGE("Can't find sessionEnded");
1230        return -1;
1231    }
1232
1233    field_context = env->GetFieldID(clazz, "mNativeContext", "J");
1234    if (field_context == NULL) {
1235        ALOGE("Can't find MtpDatabase.mNativeContext");
1236        return -1;
1237    }
1238
1239    // now set up fields for MtpPropertyList class
1240    clazz = env->FindClass("android/mtp/MtpPropertyList");
1241    if (clazz == NULL) {
1242        ALOGE("Can't find android/mtp/MtpPropertyList");
1243        return -1;
1244    }
1245    field_mCount = env->GetFieldID(clazz, "mCount", "I");
1246    if (field_mCount == NULL) {
1247        ALOGE("Can't find MtpPropertyList.mCount");
1248        return -1;
1249    }
1250    field_mResult = env->GetFieldID(clazz, "mResult", "I");
1251    if (field_mResult == NULL) {
1252        ALOGE("Can't find MtpPropertyList.mResult");
1253        return -1;
1254    }
1255    field_mObjectHandles = env->GetFieldID(clazz, "mObjectHandles", "[I");
1256    if (field_mObjectHandles == NULL) {
1257        ALOGE("Can't find MtpPropertyList.mObjectHandles");
1258        return -1;
1259    }
1260    field_mPropertyCodes = env->GetFieldID(clazz, "mPropertyCodes", "[I");
1261    if (field_mPropertyCodes == NULL) {
1262        ALOGE("Can't find MtpPropertyList.mPropertyCodes");
1263        return -1;
1264    }
1265    field_mDataTypes = env->GetFieldID(clazz, "mDataTypes", "[I");
1266    if (field_mDataTypes == NULL) {
1267        ALOGE("Can't find MtpPropertyList.mDataTypes");
1268        return -1;
1269    }
1270    field_mLongValues = env->GetFieldID(clazz, "mLongValues", "[J");
1271    if (field_mLongValues == NULL) {
1272        ALOGE("Can't find MtpPropertyList.mLongValues");
1273        return -1;
1274    }
1275    field_mStringValues = env->GetFieldID(clazz, "mStringValues", "[Ljava/lang/String;");
1276    if (field_mStringValues == NULL) {
1277        ALOGE("Can't find MtpPropertyList.mStringValues");
1278        return -1;
1279    }
1280
1281    if (AndroidRuntime::registerNativeMethods(env,
1282                "android/mtp/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods)))
1283        return -1;
1284
1285    return AndroidRuntime::registerNativeMethods(env,
1286                "android/mtp/MtpPropertyGroup", gMtpPropertyGroupMethods, NELEM(gMtpPropertyGroupMethods));
1287}
1288