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