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    char            date[20];
787    MtpString       path;
788    int64_t         length;
789    MtpObjectFormat format;
790
791    MtpResponseCode result = getObjectFilePath(handle, path, length, format);
792    if (result != MTP_RESPONSE_OK) {
793        return result;
794    }
795    info.mCompressedSize = (length > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)length);
796
797    JNIEnv* env = AndroidRuntime::getJNIEnv();
798    if (!env->CallBooleanMethod(mDatabase, method_getObjectInfo,
799                (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer)) {
800        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
801    }
802
803    jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
804    info.mStorageID = intValues[0];
805    info.mFormat = intValues[1];
806    info.mParent = intValues[2];
807    env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
808
809    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
810    info.mDateCreated = longValues[0];
811    info.mDateModified = longValues[1];
812    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
813
814//    info.mAssociationType = (format == MTP_FORMAT_ASSOCIATION ?
815//                            MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
816//                            MTP_ASSOCIATION_TYPE_UNDEFINED);
817    info.mAssociationType = MTP_ASSOCIATION_TYPE_UNDEFINED;
818
819    jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
820    MtpString temp(str);
821    info.mName = strdup((const char *)temp);
822    env->ReleaseCharArrayElements(mStringBuffer, str, 0);
823
824    // read EXIF data for thumbnail information
825    if (info.mFormat == MTP_FORMAT_EXIF_JPEG || info.mFormat == MTP_FORMAT_JFIF) {
826
827        ExifData *exifdata = exif_data_new_from_file(path);
828        if (exifdata) {
829            //exif_data_foreach_content(exifdata, foreachcontent, NULL);
830
831            // XXX get this from exif, or parse jpeg header instead?
832            ExifEntry *w = exif_content_get_entry(
833                    exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_X_DIMENSION);
834            ExifEntry *h = exif_content_get_entry(
835                    exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_Y_DIMENSION);
836            info.mThumbCompressedSize = exifdata->data ? exifdata->size : 0;
837            info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
838            info.mImagePixWidth = w ? getLongFromExifEntry(w) : 0;
839            info.mImagePixHeight = h ? getLongFromExifEntry(h) : 0;
840            exif_data_unref(exifdata);
841        }
842    }
843
844    checkAndClearExceptionFromCallback(env, __FUNCTION__);
845    return MTP_RESPONSE_OK;
846}
847
848void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
849    MtpString path;
850    int64_t length;
851    MtpObjectFormat format;
852    void* result = NULL;
853    outThumbSize = 0;
854
855    if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK
856            && (format == MTP_FORMAT_EXIF_JPEG || format == MTP_FORMAT_JFIF)) {
857
858        ExifData *exifdata = exif_data_new_from_file(path);
859        if (exifdata) {
860            if (exifdata->data) {
861                result = malloc(exifdata->size);
862                if (result) {
863                    memcpy(result, exifdata->data, exifdata->size);
864                    outThumbSize = exifdata->size;
865                }
866            }
867            exif_data_unref(exifdata);
868        }
869    }
870
871    return result;
872}
873
874MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle,
875                                            MtpString& outFilePath,
876                                            int64_t& outFileLength,
877                                            MtpObjectFormat& outFormat) {
878    JNIEnv* env = AndroidRuntime::getJNIEnv();
879    jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath,
880                (jint)handle, mStringBuffer, mLongBuffer);
881    if (result != MTP_RESPONSE_OK) {
882        checkAndClearExceptionFromCallback(env, __FUNCTION__);
883        return result;
884    }
885
886    jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
887    outFilePath.setTo(str, strlen16(str));
888    env->ReleaseCharArrayElements(mStringBuffer, str, 0);
889
890    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
891    outFileLength = longValues[0];
892    outFormat = longValues[1];
893    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
894
895    checkAndClearExceptionFromCallback(env, __FUNCTION__);
896    return result;
897}
898
899MtpResponseCode MyMtpDatabase::deleteFile(MtpObjectHandle handle) {
900    JNIEnv* env = AndroidRuntime::getJNIEnv();
901    MtpResponseCode result = env->CallIntMethod(mDatabase, method_deleteFile, (jint)handle);
902
903    checkAndClearExceptionFromCallback(env, __FUNCTION__);
904    return result;
905}
906
907struct PropertyTableEntry {
908    MtpObjectProperty   property;
909    int                 type;
910};
911
912static const PropertyTableEntry   kObjectPropertyTable[] = {
913    {   MTP_PROPERTY_STORAGE_ID,        MTP_TYPE_UINT32     },
914    {   MTP_PROPERTY_OBJECT_FORMAT,     MTP_TYPE_UINT16     },
915    {   MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16     },
916    {   MTP_PROPERTY_OBJECT_SIZE,       MTP_TYPE_UINT64     },
917    {   MTP_PROPERTY_OBJECT_FILE_NAME,  MTP_TYPE_STR        },
918    {   MTP_PROPERTY_DATE_MODIFIED,     MTP_TYPE_STR        },
919    {   MTP_PROPERTY_PARENT_OBJECT,     MTP_TYPE_UINT32     },
920    {   MTP_PROPERTY_PERSISTENT_UID,    MTP_TYPE_UINT128    },
921    {   MTP_PROPERTY_NAME,              MTP_TYPE_STR        },
922    {   MTP_PROPERTY_DISPLAY_NAME,      MTP_TYPE_STR        },
923    {   MTP_PROPERTY_DATE_ADDED,        MTP_TYPE_STR        },
924    {   MTP_PROPERTY_ARTIST,            MTP_TYPE_STR        },
925    {   MTP_PROPERTY_ALBUM_NAME,        MTP_TYPE_STR        },
926    {   MTP_PROPERTY_ALBUM_ARTIST,      MTP_TYPE_STR        },
927    {   MTP_PROPERTY_TRACK,             MTP_TYPE_UINT16     },
928    {   MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_TYPE_STR    },
929    {   MTP_PROPERTY_GENRE,             MTP_TYPE_STR        },
930    {   MTP_PROPERTY_COMPOSER,          MTP_TYPE_STR        },
931    {   MTP_PROPERTY_DURATION,          MTP_TYPE_UINT32     },
932    {   MTP_PROPERTY_DESCRIPTION,       MTP_TYPE_STR        },
933    {   MTP_PROPERTY_AUDIO_WAVE_CODEC,  MTP_TYPE_UINT32     },
934    {   MTP_PROPERTY_BITRATE_TYPE,      MTP_TYPE_UINT16     },
935    {   MTP_PROPERTY_AUDIO_BITRATE,     MTP_TYPE_UINT32     },
936    {   MTP_PROPERTY_NUMBER_OF_CHANNELS,MTP_TYPE_UINT16     },
937    {   MTP_PROPERTY_SAMPLE_RATE,       MTP_TYPE_UINT32     },
938};
939
940static const PropertyTableEntry   kDevicePropertyTable[] = {
941    {   MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,    MTP_TYPE_STR },
942    {   MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,       MTP_TYPE_STR },
943    {   MTP_DEVICE_PROPERTY_IMAGE_SIZE,                 MTP_TYPE_STR },
944    {   MTP_DEVICE_PROPERTY_BATTERY_LEVEL,              MTP_TYPE_UINT8 },
945};
946
947bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
948    int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
949    const PropertyTableEntry* entry = kObjectPropertyTable;
950    for (int i = 0; i < count; i++, entry++) {
951        if (entry->property == property) {
952            type = entry->type;
953            return true;
954        }
955    }
956    return false;
957}
958
959bool MyMtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
960    int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]);
961    const PropertyTableEntry* entry = kDevicePropertyTable;
962    for (int i = 0; i < count; i++, entry++) {
963        if (entry->property == property) {
964            type = entry->type;
965            return true;
966        }
967    }
968    return false;
969}
970
971MtpObjectHandleList* MyMtpDatabase::getObjectReferences(MtpObjectHandle handle) {
972    JNIEnv* env = AndroidRuntime::getJNIEnv();
973    jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectReferences,
974                (jint)handle);
975    if (!array)
976        return NULL;
977    MtpObjectHandleList* list = new MtpObjectHandleList();
978    jint* handles = env->GetIntArrayElements(array, 0);
979    jsize length = env->GetArrayLength(array);
980    for (int i = 0; i < length; i++)
981        list->push(handles[i]);
982    env->ReleaseIntArrayElements(array, handles, 0);
983    env->DeleteLocalRef(array);
984
985    checkAndClearExceptionFromCallback(env, __FUNCTION__);
986    return list;
987}
988
989MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle,
990                                                    MtpObjectHandleList* references) {
991    JNIEnv* env = AndroidRuntime::getJNIEnv();
992    int count = references->size();
993    jintArray array = env->NewIntArray(count);
994    if (!array) {
995        ALOGE("out of memory in setObjectReferences");
996        return false;
997    }
998    jint* handles = env->GetIntArrayElements(array, 0);
999     for (int i = 0; i < count; i++)
1000        handles[i] = (*references)[i];
1001    env->ReleaseIntArrayElements(array, handles, 0);
1002    MtpResponseCode result = env->CallIntMethod(mDatabase, method_setObjectReferences,
1003                (jint)handle, array);
1004    env->DeleteLocalRef(array);
1005
1006    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1007    return result;
1008}
1009
1010MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
1011                                            MtpObjectFormat format) {
1012    static const int channelEnum[] = {
1013                                        1,  // mono
1014                                        2,  // stereo
1015                                        3,  // 2.1
1016                                        4,  // 3
1017                                        5,  // 3.1
1018                                        6,  // 4
1019                                        7,  // 4.1
1020                                        8,  // 5
1021                                        9,  // 5.1
1022                                    };
1023    static const int bitrateEnum[] = {
1024                                        1,  // fixed rate
1025                                        2,  // variable rate
1026                                     };
1027
1028    MtpProperty* result = NULL;
1029    switch (property) {
1030        case MTP_PROPERTY_OBJECT_FORMAT:
1031            // use format as default value
1032            result = new MtpProperty(property, MTP_TYPE_UINT16, false, format);
1033            break;
1034        case MTP_PROPERTY_PROTECTION_STATUS:
1035        case MTP_PROPERTY_TRACK:
1036            result = new MtpProperty(property, MTP_TYPE_UINT16);
1037            break;
1038        case MTP_PROPERTY_STORAGE_ID:
1039        case MTP_PROPERTY_PARENT_OBJECT:
1040        case MTP_PROPERTY_DURATION:
1041        case MTP_PROPERTY_AUDIO_WAVE_CODEC:
1042            result = new MtpProperty(property, MTP_TYPE_UINT32);
1043            break;
1044        case MTP_PROPERTY_OBJECT_SIZE:
1045            result = new MtpProperty(property, MTP_TYPE_UINT64);
1046            break;
1047        case MTP_PROPERTY_PERSISTENT_UID:
1048            result = new MtpProperty(property, MTP_TYPE_UINT128);
1049            break;
1050        case MTP_PROPERTY_NAME:
1051        case MTP_PROPERTY_DISPLAY_NAME:
1052        case MTP_PROPERTY_ARTIST:
1053        case MTP_PROPERTY_ALBUM_NAME:
1054        case MTP_PROPERTY_ALBUM_ARTIST:
1055        case MTP_PROPERTY_GENRE:
1056        case MTP_PROPERTY_COMPOSER:
1057        case MTP_PROPERTY_DESCRIPTION:
1058            result = new MtpProperty(property, MTP_TYPE_STR);
1059            break;
1060        case MTP_PROPERTY_DATE_MODIFIED:
1061        case MTP_PROPERTY_DATE_ADDED:
1062        case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
1063            result = new MtpProperty(property, MTP_TYPE_STR);
1064            result->setFormDateTime();
1065            break;
1066        case MTP_PROPERTY_OBJECT_FILE_NAME:
1067            // We allow renaming files and folders
1068            result = new MtpProperty(property, MTP_TYPE_STR, true);
1069            break;
1070        case MTP_PROPERTY_BITRATE_TYPE:
1071             result = new MtpProperty(property, MTP_TYPE_UINT16);
1072            result->setFormEnum(bitrateEnum, sizeof(bitrateEnum)/sizeof(bitrateEnum[0]));
1073            break;
1074        case MTP_PROPERTY_AUDIO_BITRATE:
1075            result = new MtpProperty(property, MTP_TYPE_UINT32);
1076            result->setFormRange(1, 1536000, 1);
1077            break;
1078        case MTP_PROPERTY_NUMBER_OF_CHANNELS:
1079            result = new MtpProperty(property, MTP_TYPE_UINT16);
1080            result->setFormEnum(channelEnum, sizeof(channelEnum)/sizeof(channelEnum[0]));
1081            break;
1082        case MTP_PROPERTY_SAMPLE_RATE:
1083            result = new MtpProperty(property, MTP_TYPE_UINT32);
1084            result->setFormRange(8000, 48000, 1);
1085            break;
1086    }
1087
1088    return result;
1089}
1090
1091MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
1092    JNIEnv* env = AndroidRuntime::getJNIEnv();
1093    MtpProperty* result = NULL;
1094    bool writable = false;
1095
1096    switch (property) {
1097        case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
1098        case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
1099            writable = true;
1100            // fall through
1101        case MTP_DEVICE_PROPERTY_IMAGE_SIZE: {
1102            result = new MtpProperty(property, MTP_TYPE_STR, writable);
1103
1104            // get current value
1105            jint ret = env->CallIntMethod(mDatabase, method_getDeviceProperty,
1106                        (jint)property, mLongBuffer, mStringBuffer);
1107            if (ret == MTP_RESPONSE_OK) {
1108                jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
1109                result->setCurrentValue(str);
1110                // for read-only properties it is safe to assume current value is default value
1111                if (!writable)
1112                    result->setDefaultValue(str);
1113                env->ReleaseCharArrayElements(mStringBuffer, str, 0);
1114            } else {
1115                ALOGE("unable to read device property, response: %04X", ret);
1116            }
1117            break;
1118        }
1119        case MTP_DEVICE_PROPERTY_BATTERY_LEVEL:
1120            result = new MtpProperty(property, MTP_TYPE_UINT8);
1121            result->setFormRange(0, env->GetIntField(mDatabase, field_batteryScale), 1);
1122            result->mCurrentValue.u.u8 = (uint8_t)env->GetIntField(mDatabase, field_batteryLevel);
1123            break;
1124    }
1125
1126    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1127    return result;
1128}
1129
1130void MyMtpDatabase::sessionStarted() {
1131    JNIEnv* env = AndroidRuntime::getJNIEnv();
1132    env->CallVoidMethod(mDatabase, method_sessionStarted);
1133    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1134}
1135
1136void MyMtpDatabase::sessionEnded() {
1137    JNIEnv* env = AndroidRuntime::getJNIEnv();
1138    env->CallVoidMethod(mDatabase, method_sessionEnded);
1139    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1140}
1141
1142// ----------------------------------------------------------------------------
1143
1144static void
1145android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz)
1146{
1147    MyMtpDatabase* database = new MyMtpDatabase(env, thiz);
1148    env->SetLongField(thiz, field_context, (jlong)database);
1149    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1150}
1151
1152static void
1153android_mtp_MtpDatabase_finalize(JNIEnv *env, jobject thiz)
1154{
1155    MyMtpDatabase* database = (MyMtpDatabase *)env->GetLongField(thiz, field_context);
1156    database->cleanup(env);
1157    delete database;
1158    env->SetLongField(thiz, field_context, 0);
1159    checkAndClearExceptionFromCallback(env, __FUNCTION__);
1160}
1161
1162static jstring
1163android_mtp_MtpPropertyGroup_format_date_time(JNIEnv *env, jobject /*thiz*/, jlong seconds)
1164{
1165    char    date[20];
1166    formatDateTime(seconds, date, sizeof(date));
1167    return env->NewStringUTF(date);
1168}
1169
1170// ----------------------------------------------------------------------------
1171
1172static JNINativeMethod gMtpDatabaseMethods[] = {
1173    {"native_setup",            "()V",  (void *)android_mtp_MtpDatabase_setup},
1174    {"native_finalize",         "()V",  (void *)android_mtp_MtpDatabase_finalize},
1175};
1176
1177static JNINativeMethod gMtpPropertyGroupMethods[] = {
1178    {"format_date_time",        "(J)Ljava/lang/String;",
1179                                        (void *)android_mtp_MtpPropertyGroup_format_date_time},
1180};
1181
1182static const char* const kClassPathName = "android/mtp/MtpDatabase";
1183
1184int register_android_mtp_MtpDatabase(JNIEnv *env)
1185{
1186    jclass clazz;
1187
1188    clazz = env->FindClass("android/mtp/MtpDatabase");
1189    if (clazz == NULL) {
1190        ALOGE("Can't find android/mtp/MtpDatabase");
1191        return -1;
1192    }
1193    method_beginSendObject = env->GetMethodID(clazz, "beginSendObject", "(Ljava/lang/String;IIIJJ)I");
1194    if (method_beginSendObject == NULL) {
1195        ALOGE("Can't find beginSendObject");
1196        return -1;
1197    }
1198    method_endSendObject = env->GetMethodID(clazz, "endSendObject", "(Ljava/lang/String;IIZ)V");
1199    if (method_endSendObject == NULL) {
1200        ALOGE("Can't find endSendObject");
1201        return -1;
1202    }
1203    method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I");
1204    if (method_getObjectList == NULL) {
1205        ALOGE("Can't find getObjectList");
1206        return -1;
1207    }
1208    method_getNumObjects = env->GetMethodID(clazz, "getNumObjects", "(III)I");
1209    if (method_getNumObjects == NULL) {
1210        ALOGE("Can't find getNumObjects");
1211        return -1;
1212    }
1213    method_getSupportedPlaybackFormats = env->GetMethodID(clazz, "getSupportedPlaybackFormats", "()[I");
1214    if (method_getSupportedPlaybackFormats == NULL) {
1215        ALOGE("Can't find getSupportedPlaybackFormats");
1216        return -1;
1217    }
1218    method_getSupportedCaptureFormats = env->GetMethodID(clazz, "getSupportedCaptureFormats", "()[I");
1219    if (method_getSupportedCaptureFormats == NULL) {
1220        ALOGE("Can't find getSupportedCaptureFormats");
1221        return -1;
1222    }
1223    method_getSupportedObjectProperties = env->GetMethodID(clazz, "getSupportedObjectProperties", "(I)[I");
1224    if (method_getSupportedObjectProperties == NULL) {
1225        ALOGE("Can't find getSupportedObjectProperties");
1226        return -1;
1227    }
1228    method_getSupportedDeviceProperties = env->GetMethodID(clazz, "getSupportedDeviceProperties", "()[I");
1229    if (method_getSupportedDeviceProperties == NULL) {
1230        ALOGE("Can't find getSupportedDeviceProperties");
1231        return -1;
1232    }
1233    method_setObjectProperty = env->GetMethodID(clazz, "setObjectProperty", "(IIJLjava/lang/String;)I");
1234    if (method_setObjectProperty == NULL) {
1235        ALOGE("Can't find setObjectProperty");
1236        return -1;
1237    }
1238    method_getDeviceProperty = env->GetMethodID(clazz, "getDeviceProperty", "(I[J[C)I");
1239    if (method_getDeviceProperty == NULL) {
1240        ALOGE("Can't find getDeviceProperty");
1241        return -1;
1242    }
1243    method_setDeviceProperty = env->GetMethodID(clazz, "setDeviceProperty", "(IJLjava/lang/String;)I");
1244    if (method_setDeviceProperty == NULL) {
1245        ALOGE("Can't find setDeviceProperty");
1246        return -1;
1247    }
1248    method_getObjectPropertyList = env->GetMethodID(clazz, "getObjectPropertyList",
1249            "(JIJII)Landroid/mtp/MtpPropertyList;");
1250    if (method_getObjectPropertyList == NULL) {
1251        ALOGE("Can't find getObjectPropertyList");
1252        return -1;
1253    }
1254    method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z");
1255    if (method_getObjectInfo == NULL) {
1256        ALOGE("Can't find getObjectInfo");
1257        return -1;
1258    }
1259    method_getObjectFilePath = env->GetMethodID(clazz, "getObjectFilePath", "(I[C[J)I");
1260    if (method_getObjectFilePath == NULL) {
1261        ALOGE("Can't find getObjectFilePath");
1262        return -1;
1263    }
1264    method_deleteFile = env->GetMethodID(clazz, "deleteFile", "(I)I");
1265    if (method_deleteFile == NULL) {
1266        ALOGE("Can't find deleteFile");
1267        return -1;
1268    }
1269    method_getObjectReferences = env->GetMethodID(clazz, "getObjectReferences", "(I)[I");
1270    if (method_getObjectReferences == NULL) {
1271        ALOGE("Can't find getObjectReferences");
1272        return -1;
1273    }
1274    method_setObjectReferences = env->GetMethodID(clazz, "setObjectReferences", "(I[I)I");
1275    if (method_setObjectReferences == NULL) {
1276        ALOGE("Can't find setObjectReferences");
1277        return -1;
1278    }
1279    method_sessionStarted = env->GetMethodID(clazz, "sessionStarted", "()V");
1280    if (method_sessionStarted == NULL) {
1281        ALOGE("Can't find sessionStarted");
1282        return -1;
1283    }
1284    method_sessionEnded = env->GetMethodID(clazz, "sessionEnded", "()V");
1285    if (method_sessionEnded == NULL) {
1286        ALOGE("Can't find sessionEnded");
1287        return -1;
1288    }
1289
1290    field_context = env->GetFieldID(clazz, "mNativeContext", "J");
1291    if (field_context == NULL) {
1292        ALOGE("Can't find MtpDatabase.mNativeContext");
1293        return -1;
1294    }
1295    field_batteryLevel = env->GetFieldID(clazz, "mBatteryLevel", "I");
1296    if (field_batteryLevel == NULL) {
1297        ALOGE("Can't find MtpDatabase.mBatteryLevel");
1298        return -1;
1299    }
1300    field_batteryScale = env->GetFieldID(clazz, "mBatteryScale", "I");
1301    if (field_batteryScale == NULL) {
1302        ALOGE("Can't find MtpDatabase.mBatteryScale");
1303        return -1;
1304    }
1305
1306    // now set up fields for MtpPropertyList class
1307    clazz = env->FindClass("android/mtp/MtpPropertyList");
1308    if (clazz == NULL) {
1309        ALOGE("Can't find android/mtp/MtpPropertyList");
1310        return -1;
1311    }
1312    field_mCount = env->GetFieldID(clazz, "mCount", "I");
1313    if (field_mCount == NULL) {
1314        ALOGE("Can't find MtpPropertyList.mCount");
1315        return -1;
1316    }
1317    field_mResult = env->GetFieldID(clazz, "mResult", "I");
1318    if (field_mResult == NULL) {
1319        ALOGE("Can't find MtpPropertyList.mResult");
1320        return -1;
1321    }
1322    field_mObjectHandles = env->GetFieldID(clazz, "mObjectHandles", "[I");
1323    if (field_mObjectHandles == NULL) {
1324        ALOGE("Can't find MtpPropertyList.mObjectHandles");
1325        return -1;
1326    }
1327    field_mPropertyCodes = env->GetFieldID(clazz, "mPropertyCodes", "[I");
1328    if (field_mPropertyCodes == NULL) {
1329        ALOGE("Can't find MtpPropertyList.mPropertyCodes");
1330        return -1;
1331    }
1332    field_mDataTypes = env->GetFieldID(clazz, "mDataTypes", "[I");
1333    if (field_mDataTypes == NULL) {
1334        ALOGE("Can't find MtpPropertyList.mDataTypes");
1335        return -1;
1336    }
1337    field_mLongValues = env->GetFieldID(clazz, "mLongValues", "[J");
1338    if (field_mLongValues == NULL) {
1339        ALOGE("Can't find MtpPropertyList.mLongValues");
1340        return -1;
1341    }
1342    field_mStringValues = env->GetFieldID(clazz, "mStringValues", "[Ljava/lang/String;");
1343    if (field_mStringValues == NULL) {
1344        ALOGE("Can't find MtpPropertyList.mStringValues");
1345        return -1;
1346    }
1347
1348    if (AndroidRuntime::registerNativeMethods(env,
1349                "android/mtp/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods)))
1350        return -1;
1351
1352    return AndroidRuntime::registerNativeMethods(env,
1353                "android/mtp/MtpPropertyGroup", gMtpPropertyGroupMethods, NELEM(gMtpPropertyGroupMethods));
1354}
1355