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