OSDN Git Service

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