android_mtp_MtpDatabase.cpp revision eaabdce0ad556a91ccf731d042edfd9c5f249f5e
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "MtpDatabaseJNI"
18#include "utils/Log.h"
19
20#include <stdio.h>
21#include <assert.h>
22#include <limits.h>
23#include <unistd.h>
24#include <fcntl.h>
25
26#include "jni.h"
27#include "JNIHelp.h"
28#include "android_runtime/AndroidRuntime.h"
29#include "android_runtime/Log.h"
30
31#include "MtpDatabase.h"
32#include "MtpDataPacket.h"
33#include "MtpObjectInfo.h"
34#include "MtpProperty.h"
35#include "MtpStringBuffer.h"
36#include "MtpUtils.h"
37#include "mtp.h"
38
39extern "C" {
40#include "libexif/exif-content.h"
41#include "libexif/exif-data.h"
42#include "libexif/exif-tag.h"
43#include "libexif/exif-utils.h"
44}
45
46using namespace android;
47
48// ----------------------------------------------------------------------------
49
50static jmethodID method_beginSendObject;
51static jmethodID method_endSendObject;
52static jmethodID method_getObjectList;
53static jmethodID method_getNumObjects;
54static jmethodID method_getSupportedPlaybackFormats;
55static jmethodID method_getSupportedCaptureFormats;
56static jmethodID method_getSupportedObjectProperties;
57static jmethodID method_getSupportedDeviceProperties;
58static jmethodID method_setObjectProperty;
59static jmethodID method_getDeviceProperty;
60static jmethodID method_setDeviceProperty;
61static jmethodID method_getObjectPropertyList;
62static jmethodID method_getObjectInfo;
63static jmethodID method_getObjectFilePath;
64static jmethodID method_deleteFile;
65static jmethodID method_getObjectReferences;
66static jmethodID method_setObjectReferences;
67static jmethodID method_sessionStarted;
68static jmethodID method_sessionEnded;
69
70static jfieldID field_context;
71
72// MtpPropertyList fields
73static jfieldID field_mCount;
74static jfieldID field_mResult;
75static jfieldID field_mObjectHandles;
76static jfieldID field_mPropertyCodes;
77static jfieldID field_mDataTypes;
78static jfieldID field_mLongValues;
79static jfieldID field_mStringValues;
80
81
82MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
83    return (MtpDatabase *)env->GetLongField(database, field_context);
84}
85
86// ----------------------------------------------------------------------------
87
88class MyMtpDatabase : public MtpDatabase {
89private:
90    jobject         mDatabase;
91    jintArray       mIntBuffer;
92    jlongArray      mLongBuffer;
93    jcharArray      mStringBuffer;
94
95public:
96                                    MyMtpDatabase(JNIEnv *env, jobject client);
97    virtual                         ~MyMtpDatabase();
98    void                            cleanup(JNIEnv *env);
99
100    virtual MtpObjectHandle         beginSendObject(const char* path,
101                                            MtpObjectFormat format,
102                                            MtpObjectHandle parent,
103                                            MtpStorageID storage,
104                                            uint64_t size,
105                                            time_t modified);
106
107    virtual void                    endSendObject(const char* path,
108                                            MtpObjectHandle handle,
109                                            MtpObjectFormat format,
110                                            bool succeeded);
111
112    virtual MtpObjectHandleList*    getObjectList(MtpStorageID storageID,
113                                    MtpObjectFormat format,
114                                    MtpObjectHandle parent);
115
116    virtual int                     getNumObjects(MtpStorageID storageID,
117                                            MtpObjectFormat format,
118                                            MtpObjectHandle parent);
119
120    // callee should delete[] the results from these
121    // results can be NULL
122    virtual MtpObjectFormatList*    getSupportedPlaybackFormats();
123    virtual MtpObjectFormatList*    getSupportedCaptureFormats();
124    virtual MtpObjectPropertyList*  getSupportedObjectProperties(MtpObjectFormat format);
125    virtual MtpDevicePropertyList*  getSupportedDeviceProperties();
126
127    virtual MtpResponseCode         getObjectPropertyValue(MtpObjectHandle handle,
128                                            MtpObjectProperty property,
129                                            MtpDataPacket& packet);
130
131    virtual MtpResponseCode         setObjectPropertyValue(MtpObjectHandle handle,
132                                            MtpObjectProperty property,
133                                            MtpDataPacket& packet);
134
135    virtual MtpResponseCode         getDevicePropertyValue(MtpDeviceProperty property,
136                                            MtpDataPacket& packet);
137
138    virtual MtpResponseCode         setDevicePropertyValue(MtpDeviceProperty property,
139                                            MtpDataPacket& packet);
140
141    virtual MtpResponseCode         resetDeviceProperty(MtpDeviceProperty property);
142
143    virtual MtpResponseCode         getObjectPropertyList(MtpObjectHandle handle,
144                                            uint32_t format, uint32_t property,
145                                            int groupCode, int depth,
146                                            MtpDataPacket& packet);
147
148    virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
149                                            MtpObjectInfo& info);
150
151    virtual void*                   getThumbnail(MtpObjectHandle handle, size_t& outThumbSize);
152
153    virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
154                                            MtpString& outFilePath,
155                                            int64_t& outFileLength,
156                                            MtpObjectFormat& outFormat);
157    virtual MtpResponseCode         deleteFile(MtpObjectHandle handle);
158
159    bool                            getObjectPropertyInfo(MtpObjectProperty property, int& type);
160    bool                            getDevicePropertyInfo(MtpDeviceProperty property, int& type);
161
162    virtual MtpObjectHandleList*    getObjectReferences(MtpObjectHandle handle);
163
164    virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
165                                            MtpObjectHandleList* references);
166
167    virtual MtpProperty*            getObjectPropertyDesc(MtpObjectProperty property,
168                                            MtpObjectFormat format);
169
170    virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property);
171
172    virtual void                    sessionStarted();
173
174    virtual void                    sessionEnded();
175};
176
177// ----------------------------------------------------------------------------
178
179static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
180    if (env->ExceptionCheck()) {
181        ALOGE("An exception was thrown by callback '%s'.", methodName);
182        LOGE_EX(env);
183        env->ExceptionClear();
184    }
185}
186
187// ----------------------------------------------------------------------------
188
189MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client)
190    :   mDatabase(env->NewGlobalRef(client)),
191        mIntBuffer(NULL),
192        mLongBuffer(NULL),
193        mStringBuffer(NULL)
194{
195    // create buffers for out arguments
196    // we don't need to be thread-safe so this is OK
197    jintArray intArray = env->NewIntArray(3);
198    if (!intArray) {
199        return; // Already threw.
200    }
201    mIntBuffer = (jintArray)env->NewGlobalRef(intArray);
202    jlongArray longArray = env->NewLongArray(2);
203    if (!longArray) {
204        return; // Already threw.
205    }
206    mLongBuffer = (jlongArray)env->NewGlobalRef(longArray);
207    jcharArray charArray = env->NewCharArray(256);
208    if (!charArray) {
209        return; // Already threw.
210    }
211    mStringBuffer = (jcharArray)env->NewGlobalRef(charArray);
212}
213
214void MyMtpDatabase::cleanup(JNIEnv *env) {
215    env->DeleteGlobalRef(mDatabase);
216    env->DeleteGlobalRef(mIntBuffer);
217    env->DeleteGlobalRef(mLongBuffer);
218    env->DeleteGlobalRef(mStringBuffer);
219}
220
221MyMtpDatabase::~MyMtpDatabase() {
222}
223
224MtpObjectHandle MyMtpDatabase::beginSendObject(const char* path,
225                                            MtpObjectFormat format,
226                                            MtpObjectHandle parent,
227                                            MtpStorageID storage,
228                                            uint64_t size,
229                                            time_t modified) {
230    JNIEnv* env = AndroidRuntime::getJNIEnv();
231    jstring pathStr = env->NewStringUTF(path);
232    MtpObjectHandle result = env->CallIntMethod(mDatabase, method_beginSendObject,
233            pathStr, (jint)format, (jint)parent, (jint)storage,
234            (jlong)size, (jlong)modified);
235
236    if (pathStr)
237        env->DeleteLocalRef(pathStr);
238    checkAndClearExceptionFromCallback(env, __FUNCTION__);
239    return result;
240}
241
242void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle,
243                                MtpObjectFormat format, bool succeeded) {
244    JNIEnv* env = AndroidRuntime::getJNIEnv();
245    jstring pathStr = env->NewStringUTF(path);
246    env->CallVoidMethod(mDatabase, method_endSendObject, pathStr,
247                        (jint)handle, (jint)format, (jboolean)succeeded);
248
249    if (pathStr)
250        env->DeleteLocalRef(pathStr);
251    checkAndClearExceptionFromCallback(env, __FUNCTION__);
252}
253
254MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID,
255                                    MtpObjectFormat format,
256                                    MtpObjectHandle parent) {
257    JNIEnv* env = AndroidRuntime::getJNIEnv();
258    jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectList,
259                (jint)storageID, (jint)format, (jint)parent);
260    if (!array)
261        return NULL;
262    MtpObjectHandleList* list = new MtpObjectHandleList();
263    jint* handles = env->GetIntArrayElements(array, 0);
264    jsize length = env->GetArrayLength(array);
265    for (int i = 0; i < length; i++)
266        list->push(handles[i]);
267    env->ReleaseIntArrayElements(array, handles, 0);
268    env->DeleteLocalRef(array);
269
270    checkAndClearExceptionFromCallback(env, __FUNCTION__);
271    return list;
272}
273
274int MyMtpDatabase::getNumObjects(MtpStorageID storageID,
275                                MtpObjectFormat format,
276                                MtpObjectHandle parent) {
277    JNIEnv* env = AndroidRuntime::getJNIEnv();
278    int result = env->CallIntMethod(mDatabase, method_getNumObjects,
279                (jint)storageID, (jint)format, (jint)parent);
280
281    checkAndClearExceptionFromCallback(env, __FUNCTION__);
282    return result;
283}
284
285MtpObjectFormatList* MyMtpDatabase::getSupportedPlaybackFormats() {
286    JNIEnv* env = AndroidRuntime::getJNIEnv();
287    jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
288            method_getSupportedPlaybackFormats);
289    if (!array)
290        return NULL;
291    MtpObjectFormatList* list = new MtpObjectFormatList();
292    jint* formats = env->GetIntArrayElements(array, 0);
293    jsize length = env->GetArrayLength(array);
294    for (int i = 0; i < length; i++)
295        list->push(formats[i]);
296    env->ReleaseIntArrayElements(array, formats, 0);
297    env->DeleteLocalRef(array);
298
299    checkAndClearExceptionFromCallback(env, __FUNCTION__);
300    return list;
301}
302
303MtpObjectFormatList* MyMtpDatabase::getSupportedCaptureFormats() {
304    JNIEnv* env = AndroidRuntime::getJNIEnv();
305    jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
306            method_getSupportedCaptureFormats);
307    if (!array)
308        return NULL;
309    MtpObjectFormatList* list = new MtpObjectFormatList();
310    jint* formats = env->GetIntArrayElements(array, 0);
311    jsize length = env->GetArrayLength(array);
312    for (int i = 0; i < length; i++)
313        list->push(formats[i]);
314    env->ReleaseIntArrayElements(array, formats, 0);
315    env->DeleteLocalRef(array);
316
317    checkAndClearExceptionFromCallback(env, __FUNCTION__);
318    return list;
319}
320
321MtpObjectPropertyList* MyMtpDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
322    JNIEnv* env = AndroidRuntime::getJNIEnv();
323    jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
324            method_getSupportedObjectProperties, (jint)format);
325    if (!array)
326        return NULL;
327    MtpObjectPropertyList* list = new MtpObjectPropertyList();
328    jint* properties = env->GetIntArrayElements(array, 0);
329    jsize length = env->GetArrayLength(array);
330    for (int i = 0; i < length; i++)
331        list->push(properties[i]);
332    env->ReleaseIntArrayElements(array, properties, 0);
333    env->DeleteLocalRef(array);
334
335    checkAndClearExceptionFromCallback(env, __FUNCTION__);
336    return list;
337}
338
339MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() {
340    JNIEnv* env = AndroidRuntime::getJNIEnv();
341    jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
342            method_getSupportedDeviceProperties);
343    if (!array)
344        return NULL;
345    MtpDevicePropertyList* list = new MtpDevicePropertyList();
346    jint* properties = env->GetIntArrayElements(array, 0);
347    jsize length = env->GetArrayLength(array);
348    for (int i = 0; i < length; i++)
349        list->push(properties[i]);
350    env->ReleaseIntArrayElements(array, properties, 0);
351    env->DeleteLocalRef(array);
352
353    checkAndClearExceptionFromCallback(env, __FUNCTION__);
354    return list;
355}
356
357MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
358                                            MtpObjectProperty property,
359                                            MtpDataPacket& packet) {
360    JNIEnv* env = AndroidRuntime::getJNIEnv();
361    jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList,
362                (jlong)handle, 0, (jlong)property, 0, 0);
363    MtpResponseCode result = env->GetIntField(list, field_mResult);
364    int count = env->GetIntField(list, field_mCount);
365    if (result == MTP_RESPONSE_OK && count != 1)
366        result = MTP_RESPONSE_GENERAL_ERROR;
367
368    if (result == MTP_RESPONSE_OK) {
369        jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles);
370        jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes);
371        jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes);
372        jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues);
373        jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues);
374
375        jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0);
376        jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0);
377        jint* dataTypes = env->GetIntArrayElements(dataTypesArray, 0);
378        jlong* longValues = (longValuesArray ? env->GetLongArrayElements(longValuesArray, 0) : NULL);
379
380        int type = dataTypes[0];
381        jlong longValue = (longValues ? longValues[0] : 0);
382
383        // special case date properties, which are strings to MTP
384        // but stored internally as a uint64
385        if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) {
386            char    date[20];
387            formatDateTime(longValue, date, sizeof(date));
388            packet.putString(date);
389            goto out;
390        }
391        // release date is stored internally as just the year
392        if (property == MTP_PROPERTY_ORIGINAL_RELEASE_DATE) {
393            char    date[20];
394            snprintf(date, sizeof(date), "%04lld0101T000000", longValue);
395            packet.putString(date);
396            goto out;
397        }
398
399        switch (type) {
400            case MTP_TYPE_INT8:
401                packet.putInt8(longValue);
402                break;
403            case MTP_TYPE_UINT8:
404                packet.putUInt8(longValue);
405                break;
406            case MTP_TYPE_INT16:
407                packet.putInt16(longValue);
408                break;
409            case MTP_TYPE_UINT16:
410                packet.putUInt16(longValue);
411                break;
412            case MTP_TYPE_INT32:
413                packet.putInt32(longValue);
414                break;
415            case MTP_TYPE_UINT32:
416                packet.putUInt32(longValue);
417                break;
418            case MTP_TYPE_INT64:
419                packet.putInt64(longValue);
420                break;
421            case MTP_TYPE_UINT64:
422                packet.putUInt64(longValue);
423                break;
424            case MTP_TYPE_INT128:
425                packet.putInt128(longValue);
426                break;
427            case MTP_TYPE_UINT128:
428                packet.putInt128(longValue);
429                break;
430            case MTP_TYPE_STR:
431            {
432                jstring stringValue = (jstring)env->GetObjectArrayElement(stringValuesArray, 0);
433                if (stringValue) {
434                    const char* str = env->GetStringUTFChars(stringValue, NULL);
435                    if (str == NULL) {
436                        return MTP_RESPONSE_GENERAL_ERROR;
437                    }
438                    packet.putString(str);
439                    env->ReleaseStringUTFChars(stringValue, str);
440                } else {
441                    packet.putEmptyString();
442                }
443                break;
444             }
445            default:
446                ALOGE("unsupported type in getObjectPropertyValue\n");
447                result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
448        }
449out:
450        env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0);
451        env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0);
452        env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0);
453        if (longValues)
454            env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
455
456        env->DeleteLocalRef(objectHandlesArray);
457        env->DeleteLocalRef(propertyCodesArray);
458        env->DeleteLocalRef(dataTypesArray);
459        if (longValuesArray)
460            env->DeleteLocalRef(longValuesArray);
461        if (stringValuesArray)
462            env->DeleteLocalRef(stringValuesArray);
463    }
464
465    env->DeleteLocalRef(list);
466    checkAndClearExceptionFromCallback(env, __FUNCTION__);
467    return result;
468}
469
470MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
471                                            MtpObjectProperty property,
472                                            MtpDataPacket& packet) {
473    int         type;
474
475    if (!getObjectPropertyInfo(property, type))
476        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
477
478    JNIEnv* env = AndroidRuntime::getJNIEnv();
479    jlong longValue = 0;
480    jstring stringValue = NULL;
481
482    switch (type) {
483        case MTP_TYPE_INT8:
484            longValue = packet.getInt8();
485            break;
486        case MTP_TYPE_UINT8:
487            longValue = packet.getUInt8();
488            break;
489        case MTP_TYPE_INT16:
490            longValue = packet.getInt16();
491            break;
492        case MTP_TYPE_UINT16:
493            longValue = packet.getUInt16();
494            break;
495        case MTP_TYPE_INT32:
496            longValue = packet.getInt32();
497            break;
498        case MTP_TYPE_UINT32:
499            longValue = packet.getUInt32();
500            break;
501        case MTP_TYPE_INT64:
502            longValue = packet.getInt64();
503            break;
504        case MTP_TYPE_UINT64:
505            longValue = packet.getUInt64();
506            break;
507        case MTP_TYPE_STR:
508        {
509            MtpStringBuffer buffer;
510            packet.getString(buffer);
511            stringValue = env->NewStringUTF((const char *)buffer);
512            break;
513         }
514        default:
515            ALOGE("unsupported type in getObjectPropertyValue\n");
516            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
517    }
518
519    jint result = env->CallIntMethod(mDatabase, method_setObjectProperty,
520                (jint)handle, (jint)property, longValue, stringValue);
521    if (stringValue)
522        env->DeleteLocalRef(stringValue);
523
524    checkAndClearExceptionFromCallback(env, __FUNCTION__);
525    return result;
526}
527
528MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
529                                            MtpDataPacket& packet) {
530    int         type;
531
532    if (!getDevicePropertyInfo(property, type))
533        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
534
535    JNIEnv* env = AndroidRuntime::getJNIEnv();
536    jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty,
537                (jint)property, mLongBuffer, mStringBuffer);
538    if (result != MTP_RESPONSE_OK) {
539        checkAndClearExceptionFromCallback(env, __FUNCTION__);
540        return result;
541    }
542
543    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
544    jlong longValue = longValues[0];
545    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
546
547    switch (type) {
548        case MTP_TYPE_INT8:
549            packet.putInt8(longValue);
550            break;
551        case MTP_TYPE_UINT8:
552            packet.putUInt8(longValue);
553            break;
554        case MTP_TYPE_INT16:
555            packet.putInt16(longValue);
556            break;
557        case MTP_TYPE_UINT16:
558            packet.putUInt16(longValue);
559            break;
560        case MTP_TYPE_INT32:
561            packet.putInt32(longValue);
562            break;
563        case MTP_TYPE_UINT32:
564            packet.putUInt32(longValue);
565            break;
566        case MTP_TYPE_INT64:
567            packet.putInt64(longValue);
568            break;
569        case MTP_TYPE_UINT64:
570            packet.putUInt64(longValue);
571            break;
572        case MTP_TYPE_INT128:
573            packet.putInt128(longValue);
574            break;
575        case MTP_TYPE_UINT128:
576            packet.putInt128(longValue);
577            break;
578        case MTP_TYPE_STR:
579        {
580            jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
581            packet.putString(str);
582            env->ReleaseCharArrayElements(mStringBuffer, str, 0);
583            break;
584         }
585        default:
586            ALOGE("unsupported type in getDevicePropertyValue\n");
587            return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
588    }
589
590    checkAndClearExceptionFromCallback(env, __FUNCTION__);
591    return MTP_RESPONSE_OK;
592}
593
594MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property,
595                                            MtpDataPacket& packet) {
596    int         type;
597
598    if (!getDevicePropertyInfo(property, type))
599        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
600
601    JNIEnv* env = AndroidRuntime::getJNIEnv();
602    jlong longValue = 0;
603    jstring stringValue = NULL;
604
605    switch (type) {
606        case MTP_TYPE_INT8:
607            longValue = packet.getInt8();
608            break;
609        case MTP_TYPE_UINT8:
610            longValue = packet.getUInt8();
611            break;
612        case MTP_TYPE_INT16:
613            longValue = packet.getInt16();
614            break;
615        case MTP_TYPE_UINT16:
616            longValue = packet.getUInt16();
617            break;
618        case MTP_TYPE_INT32:
619            longValue = packet.getInt32();
620            break;
621        case MTP_TYPE_UINT32:
622            longValue = packet.getUInt32();
623            break;
624        case MTP_TYPE_INT64:
625            longValue = packet.getInt64();
626            break;
627        case MTP_TYPE_UINT64:
628            longValue = packet.getUInt64();
629            break;
630        case MTP_TYPE_STR:
631        {
632            MtpStringBuffer buffer;
633            packet.getString(buffer);
634            stringValue = env->NewStringUTF((const char *)buffer);
635            break;
636         }
637        default:
638            ALOGE("unsupported type in setDevicePropertyValue\n");
639            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
640    }
641
642    jint result = env->CallIntMethod(mDatabase, method_setDeviceProperty,
643                (jint)property, longValue, stringValue);
644    if (stringValue)
645        env->DeleteLocalRef(stringValue);
646
647    checkAndClearExceptionFromCallback(env, __FUNCTION__);
648    return result;
649}
650
651MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) {
652    return -1;
653}
654
655MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle,
656                                            uint32_t format, uint32_t property,
657                                            int groupCode, int depth,
658                                            MtpDataPacket& packet) {
659    JNIEnv* env = AndroidRuntime::getJNIEnv();
660    jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList,
661                (jlong)handle, (jint)format, (jlong)property, (jint)groupCode, (jint)depth);
662    checkAndClearExceptionFromCallback(env, __FUNCTION__);
663    if (!list)
664        return MTP_RESPONSE_GENERAL_ERROR;
665    int count = env->GetIntField(list, field_mCount);
666    MtpResponseCode result = env->GetIntField(list, field_mResult);
667
668    packet.putUInt32(count);
669    if (count > 0) {
670        jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles);
671        jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes);
672        jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes);
673        jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues);
674        jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues);
675
676        jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0);
677        jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0);
678        jint* dataTypes = env->GetIntArrayElements(dataTypesArray, 0);
679        jlong* longValues = (longValuesArray ? env->GetLongArrayElements(longValuesArray, 0) : NULL);
680
681        for (int i = 0; i < count; i++) {
682            packet.putUInt32(objectHandles[i]);
683            packet.putUInt16(propertyCodes[i]);
684            int type = dataTypes[i];
685            packet.putUInt16(type);
686
687            switch (type) {
688                case MTP_TYPE_INT8:
689                    packet.putInt8(longValues[i]);
690                    break;
691                case MTP_TYPE_UINT8:
692                    packet.putUInt8(longValues[i]);
693                    break;
694                case MTP_TYPE_INT16:
695                    packet.putInt16(longValues[i]);
696                    break;
697                case MTP_TYPE_UINT16:
698                    packet.putUInt16(longValues[i]);
699                    break;
700                case MTP_TYPE_INT32:
701                    packet.putInt32(longValues[i]);
702                    break;
703                case MTP_TYPE_UINT32:
704                    packet.putUInt32(longValues[i]);
705                    break;
706                case MTP_TYPE_INT64:
707                    packet.putInt64(longValues[i]);
708                    break;
709                case MTP_TYPE_UINT64:
710                    packet.putUInt64(longValues[i]);
711                    break;
712                case MTP_TYPE_INT128:
713                    packet.putInt128(longValues[i]);
714                    break;
715                case MTP_TYPE_UINT128:
716                    packet.putUInt128(longValues[i]);
717                    break;
718                case MTP_TYPE_STR: {
719                    jstring value = (jstring)env->GetObjectArrayElement(stringValuesArray, i);
720                    const char *valueStr = (value ? env->GetStringUTFChars(value, NULL) : NULL);
721                    if (valueStr) {
722                        packet.putString(valueStr);
723                        env->ReleaseStringUTFChars(value, valueStr);
724                    } else {
725                        packet.putEmptyString();
726                    }
727                    env->DeleteLocalRef(value);
728                    break;
729                }
730                default:
731                    ALOGE("bad or unsupported data type in MyMtpDatabase::getObjectPropertyList");
732                    break;
733            }
734        }
735
736        env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0);
737        env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0);
738        env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0);
739        if (longValues)
740            env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
741
742        env->DeleteLocalRef(objectHandlesArray);
743        env->DeleteLocalRef(propertyCodesArray);
744        env->DeleteLocalRef(dataTypesArray);
745        if (longValuesArray)
746            env->DeleteLocalRef(longValuesArray);
747        if (stringValuesArray)
748            env->DeleteLocalRef(stringValuesArray);
749    }
750
751    env->DeleteLocalRef(list);
752    checkAndClearExceptionFromCallback(env, __FUNCTION__);
753    return result;
754}
755
756static void foreachentry(ExifEntry *entry, void *user) {
757    char buf[1024];
758    ALOGI("entry %x, format %d, size %d: %s",
759            entry->tag, entry->format, entry->size, exif_entry_get_value(entry, buf, sizeof(buf)));
760}
761
762static void foreachcontent(ExifContent *content, void *user) {
763    ALOGI("content %d", exif_content_get_ifd(content));
764    exif_content_foreach_entry(content, foreachentry, user);
765}
766
767static long getLongFromExifEntry(ExifEntry *e) {
768    ExifByteOrder o = exif_data_get_byte_order(e->parent->parent);
769    return exif_get_long(e->data, o);
770}
771
772MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
773                                            MtpObjectInfo& info) {
774    char            date[20];
775    MtpString       path;
776    int64_t         length;
777    MtpObjectFormat format;
778
779    MtpResponseCode result = getObjectFilePath(handle, path, length, format);
780    if (result != MTP_RESPONSE_OK) {
781        return result;
782    }
783    info.mCompressedSize = (length > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)length);
784
785    JNIEnv* env = AndroidRuntime::getJNIEnv();
786    if (!env->CallBooleanMethod(mDatabase, method_getObjectInfo,
787                (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer)) {
788        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
789    }
790
791    jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
792    info.mStorageID = intValues[0];
793    info.mFormat = intValues[1];
794    info.mParent = intValues[2];
795    env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
796
797    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
798    info.mDateCreated = longValues[0];
799    info.mDateModified = longValues[1];
800    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
801
802//    info.mAssociationType = (format == MTP_FORMAT_ASSOCIATION ?
803//                            MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
804//                            MTP_ASSOCIATION_TYPE_UNDEFINED);
805    info.mAssociationType = MTP_ASSOCIATION_TYPE_UNDEFINED;
806
807    jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
808    MtpString temp(str);
809    info.mName = strdup((const char *)temp);
810    env->ReleaseCharArrayElements(mStringBuffer, str, 0);
811
812    // read EXIF data for thumbnail information
813    if (info.mFormat == MTP_FORMAT_EXIF_JPEG || info.mFormat == MTP_FORMAT_JFIF) {
814
815        ExifData *exifdata = exif_data_new_from_file(path);
816        if (exifdata) {
817            //exif_data_foreach_content(exifdata, foreachcontent, NULL);
818
819            // XXX get this from exif, or parse jpeg header instead?
820            ExifEntry *w = exif_content_get_entry(
821                    exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_X_DIMENSION);
822            ExifEntry *h = exif_content_get_entry(
823                    exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_Y_DIMENSION);
824            info.mThumbCompressedSize = exifdata->data ? exifdata->size : 0;
825            info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
826            info.mImagePixWidth = w ? getLongFromExifEntry(w) : 0;
827            info.mImagePixHeight = h ? getLongFromExifEntry(h) : 0;
828            exif_data_unref(exifdata);
829        }
830    }
831
832    checkAndClearExceptionFromCallback(env, __FUNCTION__);
833    return MTP_RESPONSE_OK;
834}
835
836void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
837    MtpString path;
838    int64_t length;
839    MtpObjectFormat format;
840    void* result = NULL;
841    outThumbSize = 0;
842
843    if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK
844            && (format == MTP_FORMAT_EXIF_JPEG || format == MTP_FORMAT_JFIF)) {
845
846        ExifData *exifdata = exif_data_new_from_file(path);
847        if (exifdata) {
848            if (exifdata->data) {
849                result = malloc(exifdata->size);
850                if (result) {
851                    memcpy(result, exifdata->data, exifdata->size);
852                    outThumbSize = 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