• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_Streams.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 
getMtpDatabase(JNIEnv * env,jobject database)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 
checkAndClearExceptionFromCallback(JNIEnv * env,const char * methodName)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 
MtpDatabase(JNIEnv * env,jobject client)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 
cleanup(JNIEnv * env)235 void MtpDatabase::cleanup(JNIEnv *env) {
236     env->DeleteGlobalRef(mDatabase);
237     env->DeleteGlobalRef(mIntBuffer);
238     env->DeleteGlobalRef(mLongBuffer);
239     env->DeleteGlobalRef(mStringBuffer);
240 }
241 
~MtpDatabase()242 MtpDatabase::~MtpDatabase() {
243 }
244 
beginSendObject(const char * path,MtpObjectFormat format,MtpObjectHandle parent,MtpStorageID storage)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 
endSendObject(MtpObjectHandle handle,bool succeeded)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 
rescanFile(const char * path,MtpObjectHandle handle,MtpObjectFormat format)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 
getObjectList(MtpStorageID storageID,MtpObjectFormat format,MtpObjectHandle parent)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 
getNumObjects(MtpStorageID storageID,MtpObjectFormat format,MtpObjectHandle parent)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 
getSupportedPlaybackFormats()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 
getSupportedCaptureFormats()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 
getSupportedObjectProperties(MtpObjectFormat format)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 
getSupportedDeviceProperties()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 
getObjectPropertyValue(MtpObjectHandle handle,MtpObjectProperty property,MtpDataPacket & packet)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 
readLongValue(int type,MtpDataPacket & packet,jlong & longValue)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 
setObjectPropertyValue(MtpObjectHandle handle,MtpObjectProperty property,MtpDataPacket & packet)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 
getDevicePropertyValue(MtpDeviceProperty property,MtpDataPacket & packet)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 
setDevicePropertyValue(MtpDeviceProperty property,MtpDataPacket & packet)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 
resetDeviceProperty(MtpDeviceProperty)667 MtpResponseCode MtpDatabase::resetDeviceProperty(MtpDeviceProperty /*property*/) {
668     return -1;
669 }
670 
getObjectPropertyList(MtpObjectHandle handle,uint32_t format,uint32_t property,int groupCode,int depth,MtpDataPacket & packet)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             if (type == MTP_TYPE_STR) {
712                 jstring value = (jstring)env->GetObjectArrayElement(stringValuesArray, i);
713                 const char *valueStr = (value ? env->GetStringUTFChars(value, NULL) : NULL);
714                 if (valueStr) {
715                     packet.putString(valueStr);
716                     env->ReleaseStringUTFChars(value, valueStr);
717                 } else {
718                     packet.putEmptyString();
719                 }
720                 env->DeleteLocalRef(value);
721                 continue;
722             }
723 
724             if (!longValues) {
725                 ALOGE("bad longValuesArray value in MyMtpDatabase::getObjectPropertyList");
726                 continue;
727             }
728 
729             switch (type) {
730                 case MTP_TYPE_INT8:
731                     packet.putInt8(longValues[i]);
732                     break;
733                 case MTP_TYPE_UINT8:
734                     packet.putUInt8(longValues[i]);
735                     break;
736                 case MTP_TYPE_INT16:
737                     packet.putInt16(longValues[i]);
738                     break;
739                 case MTP_TYPE_UINT16:
740                     packet.putUInt16(longValues[i]);
741                     break;
742                 case MTP_TYPE_INT32:
743                     packet.putInt32(longValues[i]);
744                     break;
745                 case MTP_TYPE_UINT32:
746                     packet.putUInt32(longValues[i]);
747                     break;
748                 case MTP_TYPE_INT64:
749                     packet.putInt64(longValues[i]);
750                     break;
751                 case MTP_TYPE_UINT64:
752                     packet.putUInt64(longValues[i]);
753                     break;
754                 case MTP_TYPE_INT128:
755                     packet.putInt128(longValues[i]);
756                     break;
757                 case MTP_TYPE_UINT128:
758                     packet.putUInt128(longValues[i]);
759                     break;
760                 default:
761                     ALOGE("bad or unsupported data type in MtpDatabase::getObjectPropertyList");
762                     break;
763             }
764         }
765 
766         env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0);
767         env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0);
768         env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0);
769         env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
770 
771         env->DeleteLocalRef(objectHandlesArray);
772         env->DeleteLocalRef(propertyCodesArray);
773         env->DeleteLocalRef(dataTypesArray);
774         env->DeleteLocalRef(longValuesArray);
775         env->DeleteLocalRef(stringValuesArray);
776     }
777 
778     env->DeleteLocalRef(list);
779     checkAndClearExceptionFromCallback(env, __FUNCTION__);
780     return result;
781 }
782 
foreachentry(ExifEntry * entry,void *)783 static void foreachentry(ExifEntry *entry, void* /* user */) {
784     char buf[1024];
785     ALOGI("entry %x, format %d, size %d: %s",
786             entry->tag, entry->format, entry->size, exif_entry_get_value(entry, buf, sizeof(buf)));
787 }
788 
foreachcontent(ExifContent * content,void * user)789 static void foreachcontent(ExifContent *content, void *user) {
790     ALOGI("content %d", exif_content_get_ifd(content));
791     exif_content_foreach_entry(content, foreachentry, user);
792 }
793 
getLongFromExifEntry(ExifEntry * e)794 static long getLongFromExifEntry(ExifEntry *e) {
795     ExifByteOrder o = exif_data_get_byte_order(e->parent->parent);
796     return exif_get_long(e->data, o);
797 }
798 
getExifFromExtractor(const char * path)799 static ExifData *getExifFromExtractor(const char *path) {
800     std::unique_ptr<uint8_t[]> exifBuf;
801     ExifData *exifdata = NULL;
802 
803     FILE *fp = fopen (path, "rb");
804     if (!fp) {
805         ALOGE("failed to open file");
806         return NULL;
807     }
808 
809     sp<NuMediaExtractor> extractor = new NuMediaExtractor();
810     fseek(fp, 0L, SEEK_END);
811     if (extractor->setDataSource(fileno(fp), 0, ftell(fp)) != OK) {
812         ALOGE("failed to setDataSource");
813         fclose(fp);
814         return NULL;
815     }
816 
817     off64_t offset;
818     size_t size;
819     if (extractor->getExifOffsetSize(&offset, &size) != OK) {
820         fclose(fp);
821         return NULL;
822     }
823 
824     exifBuf.reset(new uint8_t[size]);
825     fseek(fp, offset, SEEK_SET);
826     if (fread(exifBuf.get(), 1, size, fp) == size) {
827         exifdata = exif_data_new_from_data(exifBuf.get(), size);
828     }
829 
830     fclose(fp);
831     return exifdata;
832 }
833 
getObjectInfo(MtpObjectHandle handle,MtpObjectInfo & info)834 MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
835                                              MtpObjectInfo& info) {
836     MtpStringBuffer path;
837     int64_t         length;
838     MtpObjectFormat format;
839 
840     MtpResponseCode result = getObjectFilePath(handle, path, length, format);
841     if (result != MTP_RESPONSE_OK) {
842         return result;
843     }
844     info.mCompressedSize = (length > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)length);
845 
846     JNIEnv* env = AndroidRuntime::getJNIEnv();
847     if (!env->CallBooleanMethod(mDatabase, method_getObjectInfo,
848                 (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer)) {
849         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
850     }
851 
852     jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
853     info.mStorageID = intValues[0];
854     info.mFormat = intValues[1];
855     info.mParent = intValues[2];
856     env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
857 
858     jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
859     info.mDateCreated = longValues[0];
860     info.mDateModified = longValues[1];
861     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
862 
863     if ((false)) {
864         info.mAssociationType = (format == MTP_FORMAT_ASSOCIATION ?
865                                 MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
866                                 MTP_ASSOCIATION_TYPE_UNDEFINED);
867     }
868     info.mAssociationType = MTP_ASSOCIATION_TYPE_UNDEFINED;
869 
870     jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
871     MtpStringBuffer temp(str);
872     info.mName = strdup(temp);
873     env->ReleaseCharArrayElements(mStringBuffer, str, 0);
874 
875     // read EXIF data for thumbnail information
876     switch (info.mFormat) {
877         case MTP_FORMAT_EXIF_JPEG:
878         case MTP_FORMAT_HEIF:
879         case MTP_FORMAT_JFIF: {
880             ExifData *exifdata;
881             if (info.mFormat == MTP_FORMAT_HEIF) {
882                 exifdata = getExifFromExtractor(path);
883             } else {
884                 exifdata = exif_data_new_from_file(path);
885             }
886             if (exifdata) {
887                 if ((false)) {
888                     exif_data_foreach_content(exifdata, foreachcontent, NULL);
889                 }
890 
891                 ExifEntry *w = exif_content_get_entry(
892                         exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_X_DIMENSION);
893                 ExifEntry *h = exif_content_get_entry(
894                         exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_Y_DIMENSION);
895                 info.mThumbCompressedSize = exifdata->data ? exifdata->size : 0;
896                 info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
897                 info.mImagePixWidth = w ? getLongFromExifEntry(w) : 0;
898                 info.mImagePixHeight = h ? getLongFromExifEntry(h) : 0;
899                 exif_data_unref(exifdata);
900             }
901             break;
902         }
903 
904         // Except DNG, all supported RAW image formats are not defined in PTP 1.2 specification.
905         // Most of RAW image formats are based on TIFF or TIFF/EP. To render Fuji's RAF format,
906         // it checks MTP_FORMAT_DEFINED case since it's designed as a custom format.
907         case MTP_FORMAT_DNG:
908         case MTP_FORMAT_TIFF:
909         case MTP_FORMAT_TIFF_EP:
910         case MTP_FORMAT_DEFINED: {
911             String8 temp(path);
912             std::unique_ptr<FileStream> stream(new FileStream(temp));
913             piex::PreviewImageData image_data;
914             if (!GetExifFromRawImage(stream.get(), temp, image_data)) {
915                 // Couldn't parse EXIF data from a image file via piex.
916                 break;
917             }
918 
919             info.mThumbCompressedSize = image_data.thumbnail.length;
920             info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
921             info.mImagePixWidth = image_data.full_width;
922             info.mImagePixHeight = image_data.full_height;
923 
924             break;
925         }
926     }
927 
928     checkAndClearExceptionFromCallback(env, __FUNCTION__);
929     return MTP_RESPONSE_OK;
930 }
931 
getThumbnail(MtpObjectHandle handle,size_t & outThumbSize)932 void* MtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
933     MtpStringBuffer path;
934     int64_t length;
935     MtpObjectFormat format;
936     void* result = NULL;
937     outThumbSize = 0;
938 
939     if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK) {
940         switch (format) {
941             case MTP_FORMAT_EXIF_JPEG:
942             case MTP_FORMAT_HEIF:
943             case MTP_FORMAT_JFIF: {
944                 ExifData *exifdata;
945                 if (format == MTP_FORMAT_HEIF) {
946                     exifdata = getExifFromExtractor(path);
947                 } else {
948                     exifdata = exif_data_new_from_file(path);
949                 }
950                 if (exifdata) {
951                     if (exifdata->data) {
952                         result = malloc(exifdata->size);
953                         if (result) {
954                             memcpy(result, exifdata->data, exifdata->size);
955                             outThumbSize = exifdata->size;
956                         }
957                     }
958                     exif_data_unref(exifdata);
959                 }
960                 break;
961             }
962 
963             // See the above comment on getObjectInfo() method.
964             case MTP_FORMAT_DNG:
965             case MTP_FORMAT_TIFF:
966             case MTP_FORMAT_TIFF_EP:
967             case MTP_FORMAT_DEFINED: {
968                 String8 temp(path);
969                 std::unique_ptr<FileStream> stream(new FileStream(temp));
970                 piex::PreviewImageData image_data;
971                 if (!GetExifFromRawImage(stream.get(), temp, image_data)) {
972                     // Couldn't parse EXIF data from a image file via piex.
973                     break;
974                 }
975 
976                 if (image_data.thumbnail.length == 0
977                         || image_data.thumbnail.format != ::piex::Image::kJpegCompressed) {
978                     // No thumbnail or non jpeg thumbnail.
979                     break;
980                 }
981 
982                 result = malloc(image_data.thumbnail.length);
983                 if (result) {
984                     piex::Error err = stream.get()->GetData(
985                             image_data.thumbnail.offset,
986                             image_data.thumbnail.length,
987                             (std::uint8_t *)result);
988                     if (err == piex::Error::kOk) {
989                         outThumbSize = image_data.thumbnail.length;
990                     } else {
991                         free(result);
992                         result = NULL;
993                     }
994                 }
995                 break;
996             }
997         }
998     }
999 
1000     return result;
1001 }
1002 
getObjectFilePath(MtpObjectHandle handle,MtpStringBuffer & outFilePath,int64_t & outFileLength,MtpObjectFormat & outFormat)1003 MtpResponseCode MtpDatabase::getObjectFilePath(MtpObjectHandle handle,
1004                                                  MtpStringBuffer& outFilePath,
1005                                                  int64_t& outFileLength,
1006                                                  MtpObjectFormat& outFormat) {
1007     JNIEnv* env = AndroidRuntime::getJNIEnv();
1008     jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath,
1009                 (jint)handle, mStringBuffer, mLongBuffer);
1010     if (result != MTP_RESPONSE_OK) {
1011         checkAndClearExceptionFromCallback(env, __FUNCTION__);
1012         return result;
1013     }
1014 
1015     jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
1016     outFilePath.set(str);
1017     env->ReleaseCharArrayElements(mStringBuffer, str, 0);
1018 
1019     jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
1020     outFileLength = longValues[0];
1021     outFormat = longValues[1];
1022     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
1023 
1024     checkAndClearExceptionFromCallback(env, __FUNCTION__);
1025     return result;
1026 }
1027 
beginDeleteObject(MtpObjectHandle handle)1028 MtpResponseCode MtpDatabase::beginDeleteObject(MtpObjectHandle handle) {
1029     JNIEnv* env = AndroidRuntime::getJNIEnv();
1030     MtpResponseCode result = env->CallIntMethod(mDatabase, method_beginDeleteObject, (jint)handle);
1031 
1032     checkAndClearExceptionFromCallback(env, __FUNCTION__);
1033     return result;
1034 }
1035 
endDeleteObject(MtpObjectHandle handle,bool succeeded)1036 void MtpDatabase::endDeleteObject(MtpObjectHandle handle, bool succeeded) {
1037     JNIEnv* env = AndroidRuntime::getJNIEnv();
1038     env->CallVoidMethod(mDatabase, method_endDeleteObject, (jint)handle, (jboolean) succeeded);
1039 
1040     checkAndClearExceptionFromCallback(env, __FUNCTION__);
1041 }
1042 
beginMoveObject(MtpObjectHandle handle,MtpObjectHandle newParent,MtpStorageID newStorage)1043 MtpResponseCode MtpDatabase::beginMoveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
1044         MtpStorageID newStorage) {
1045     JNIEnv* env = AndroidRuntime::getJNIEnv();
1046     MtpResponseCode result = env->CallIntMethod(mDatabase, method_beginMoveObject,
1047                 (jint)handle, (jint)newParent, (jint) newStorage);
1048 
1049     checkAndClearExceptionFromCallback(env, __FUNCTION__);
1050     return result;
1051 }
1052 
endMoveObject(MtpObjectHandle oldParent,MtpObjectHandle newParent,MtpStorageID oldStorage,MtpStorageID newStorage,MtpObjectHandle handle,bool succeeded)1053 void MtpDatabase::endMoveObject(MtpObjectHandle oldParent, MtpObjectHandle newParent,
1054                                             MtpStorageID oldStorage, MtpStorageID newStorage,
1055                                              MtpObjectHandle handle, bool succeeded) {
1056     JNIEnv* env = AndroidRuntime::getJNIEnv();
1057     env->CallVoidMethod(mDatabase, method_endMoveObject,
1058                 (jint)oldParent, (jint) newParent, (jint) oldStorage, (jint) newStorage,
1059                 (jint) handle, (jboolean) succeeded);
1060 
1061     checkAndClearExceptionFromCallback(env, __FUNCTION__);
1062 }
1063 
beginCopyObject(MtpObjectHandle handle,MtpObjectHandle newParent,MtpStorageID newStorage)1064 MtpResponseCode MtpDatabase::beginCopyObject(MtpObjectHandle handle, MtpObjectHandle newParent,
1065         MtpStorageID newStorage) {
1066     JNIEnv* env = AndroidRuntime::getJNIEnv();
1067     MtpResponseCode result = env->CallIntMethod(mDatabase, method_beginCopyObject,
1068                 (jint)handle, (jint)newParent, (jint) newStorage);
1069 
1070     checkAndClearExceptionFromCallback(env, __FUNCTION__);
1071     return result;
1072 }
1073 
endCopyObject(MtpObjectHandle handle,bool succeeded)1074 void MtpDatabase::endCopyObject(MtpObjectHandle handle, bool succeeded) {
1075     JNIEnv* env = AndroidRuntime::getJNIEnv();
1076     env->CallVoidMethod(mDatabase, method_endCopyObject, (jint)handle, (jboolean)succeeded);
1077 
1078     checkAndClearExceptionFromCallback(env, __FUNCTION__);
1079 }
1080 
1081 
1082 struct PropertyTableEntry {
1083     MtpObjectProperty   property;
1084     int                 type;
1085 };
1086 
1087 static const PropertyTableEntry   kObjectPropertyTable[] = {
1088     {   MTP_PROPERTY_STORAGE_ID,        MTP_TYPE_UINT32     },
1089     {   MTP_PROPERTY_OBJECT_FORMAT,     MTP_TYPE_UINT16     },
1090     {   MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16     },
1091     {   MTP_PROPERTY_OBJECT_SIZE,       MTP_TYPE_UINT64     },
1092     {   MTP_PROPERTY_OBJECT_FILE_NAME,  MTP_TYPE_STR        },
1093     {   MTP_PROPERTY_DATE_MODIFIED,     MTP_TYPE_STR        },
1094     {   MTP_PROPERTY_PARENT_OBJECT,     MTP_TYPE_UINT32     },
1095     {   MTP_PROPERTY_PERSISTENT_UID,    MTP_TYPE_UINT128    },
1096     {   MTP_PROPERTY_NAME,              MTP_TYPE_STR        },
1097     {   MTP_PROPERTY_DISPLAY_NAME,      MTP_TYPE_STR        },
1098     {   MTP_PROPERTY_DATE_ADDED,        MTP_TYPE_STR        },
1099     {   MTP_PROPERTY_ARTIST,            MTP_TYPE_STR        },
1100     {   MTP_PROPERTY_ALBUM_NAME,        MTP_TYPE_STR        },
1101     {   MTP_PROPERTY_ALBUM_ARTIST,      MTP_TYPE_STR        },
1102     {   MTP_PROPERTY_TRACK,             MTP_TYPE_UINT16     },
1103     {   MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_TYPE_STR    },
1104     {   MTP_PROPERTY_GENRE,             MTP_TYPE_STR        },
1105     {   MTP_PROPERTY_COMPOSER,          MTP_TYPE_STR        },
1106     {   MTP_PROPERTY_DURATION,          MTP_TYPE_UINT32     },
1107     {   MTP_PROPERTY_DESCRIPTION,       MTP_TYPE_STR        },
1108     {   MTP_PROPERTY_AUDIO_WAVE_CODEC,  MTP_TYPE_UINT32     },
1109     {   MTP_PROPERTY_BITRATE_TYPE,      MTP_TYPE_UINT16     },
1110     {   MTP_PROPERTY_AUDIO_BITRATE,     MTP_TYPE_UINT32     },
1111     {   MTP_PROPERTY_NUMBER_OF_CHANNELS,MTP_TYPE_UINT16     },
1112     {   MTP_PROPERTY_SAMPLE_RATE,       MTP_TYPE_UINT32     },
1113 };
1114 
1115 static const PropertyTableEntry   kDevicePropertyTable[] = {
1116     {   MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,    MTP_TYPE_STR },
1117     {   MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,       MTP_TYPE_STR },
1118     {   MTP_DEVICE_PROPERTY_IMAGE_SIZE,                 MTP_TYPE_STR },
1119     {   MTP_DEVICE_PROPERTY_BATTERY_LEVEL,              MTP_TYPE_UINT8 },
1120     {   MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE,      MTP_TYPE_UINT32 },
1121 };
1122 
getObjectPropertyInfo(MtpObjectProperty property,int & type)1123 bool MtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
1124     int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
1125     const PropertyTableEntry* entry = kObjectPropertyTable;
1126     for (int i = 0; i < count; i++, entry++) {
1127         if (entry->property == property) {
1128             type = entry->type;
1129             return true;
1130         }
1131     }
1132     return false;
1133 }
1134 
getDevicePropertyInfo(MtpDeviceProperty property,int & type)1135 bool MtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
1136     int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]);
1137     const PropertyTableEntry* entry = kDevicePropertyTable;
1138     for (int i = 0; i < count; i++, entry++) {
1139         if (entry->property == property) {
1140             type = entry->type;
1141             return true;
1142         }
1143     }
1144     return false;
1145 }
1146 
getObjectReferences(MtpObjectHandle handle)1147 MtpObjectHandleList* MtpDatabase::getObjectReferences(MtpObjectHandle handle) {
1148     JNIEnv* env = AndroidRuntime::getJNIEnv();
1149     jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectReferences,
1150                 (jint)handle);
1151     if (!array)
1152         return NULL;
1153     MtpObjectHandleList* list = new MtpObjectHandleList();
1154     jint* handles = env->GetIntArrayElements(array, 0);
1155     jsize length = env->GetArrayLength(array);
1156     for (int i = 0; i < length; i++)
1157         list->push_back(handles[i]);
1158     env->ReleaseIntArrayElements(array, handles, 0);
1159     env->DeleteLocalRef(array);
1160 
1161     checkAndClearExceptionFromCallback(env, __FUNCTION__);
1162     return list;
1163 }
1164 
setObjectReferences(MtpObjectHandle handle,MtpObjectHandleList * references)1165 MtpResponseCode MtpDatabase::setObjectReferences(MtpObjectHandle handle,
1166                                                    MtpObjectHandleList* references) {
1167     JNIEnv* env = AndroidRuntime::getJNIEnv();
1168     int count = references->size();
1169     jintArray array = env->NewIntArray(count);
1170     if (!array) {
1171         ALOGE("out of memory in setObjectReferences");
1172         return false;
1173     }
1174     jint* handles = env->GetIntArrayElements(array, 0);
1175      for (int i = 0; i < count; i++)
1176         handles[i] = (*references)[i];
1177     env->ReleaseIntArrayElements(array, handles, 0);
1178     MtpResponseCode result = env->CallIntMethod(mDatabase, method_setObjectReferences,
1179                 (jint)handle, array);
1180     env->DeleteLocalRef(array);
1181 
1182     checkAndClearExceptionFromCallback(env, __FUNCTION__);
1183     return result;
1184 }
1185 
getObjectPropertyDesc(MtpObjectProperty property,MtpObjectFormat format)1186 MtpProperty* MtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
1187                                                   MtpObjectFormat format) {
1188     static const int channelEnum[] = {
1189                                         1,  // mono
1190                                         2,  // stereo
1191                                         3,  // 2.1
1192                                         4,  // 3
1193                                         5,  // 3.1
1194                                         6,  // 4
1195                                         7,  // 4.1
1196                                         8,  // 5
1197                                         9,  // 5.1
1198                                     };
1199     static const int bitrateEnum[] = {
1200                                         1,  // fixed rate
1201                                         2,  // variable rate
1202                                      };
1203 
1204     MtpProperty* result = NULL;
1205     switch (property) {
1206         case MTP_PROPERTY_OBJECT_FORMAT:
1207             // use format as default value
1208             result = new MtpProperty(property, MTP_TYPE_UINT16, false, format);
1209             break;
1210         case MTP_PROPERTY_PROTECTION_STATUS:
1211         case MTP_PROPERTY_TRACK:
1212             result = new MtpProperty(property, MTP_TYPE_UINT16);
1213             break;
1214         case MTP_PROPERTY_STORAGE_ID:
1215         case MTP_PROPERTY_PARENT_OBJECT:
1216         case MTP_PROPERTY_DURATION:
1217         case MTP_PROPERTY_AUDIO_WAVE_CODEC:
1218             result = new MtpProperty(property, MTP_TYPE_UINT32);
1219             break;
1220         case MTP_PROPERTY_OBJECT_SIZE:
1221             result = new MtpProperty(property, MTP_TYPE_UINT64);
1222             break;
1223         case MTP_PROPERTY_PERSISTENT_UID:
1224             result = new MtpProperty(property, MTP_TYPE_UINT128);
1225             break;
1226         case MTP_PROPERTY_NAME:
1227         case MTP_PROPERTY_DISPLAY_NAME:
1228         case MTP_PROPERTY_ARTIST:
1229         case MTP_PROPERTY_ALBUM_NAME:
1230         case MTP_PROPERTY_ALBUM_ARTIST:
1231         case MTP_PROPERTY_GENRE:
1232         case MTP_PROPERTY_COMPOSER:
1233         case MTP_PROPERTY_DESCRIPTION:
1234             result = new MtpProperty(property, MTP_TYPE_STR);
1235             break;
1236         case MTP_PROPERTY_DATE_MODIFIED:
1237         case MTP_PROPERTY_DATE_ADDED:
1238         case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
1239             result = new MtpProperty(property, MTP_TYPE_STR);
1240             result->setFormDateTime();
1241             break;
1242         case MTP_PROPERTY_OBJECT_FILE_NAME:
1243             // We allow renaming files and folders
1244             result = new MtpProperty(property, MTP_TYPE_STR, true);
1245             break;
1246         case MTP_PROPERTY_BITRATE_TYPE:
1247              result = new MtpProperty(property, MTP_TYPE_UINT16);
1248             result->setFormEnum(bitrateEnum, sizeof(bitrateEnum)/sizeof(bitrateEnum[0]));
1249             break;
1250         case MTP_PROPERTY_AUDIO_BITRATE:
1251             result = new MtpProperty(property, MTP_TYPE_UINT32);
1252             result->setFormRange(1, 1536000, 1);
1253             break;
1254         case MTP_PROPERTY_NUMBER_OF_CHANNELS:
1255             result = new MtpProperty(property, MTP_TYPE_UINT16);
1256             result->setFormEnum(channelEnum, sizeof(channelEnum)/sizeof(channelEnum[0]));
1257             break;
1258         case MTP_PROPERTY_SAMPLE_RATE:
1259             result = new MtpProperty(property, MTP_TYPE_UINT32);
1260             result->setFormRange(8000, 48000, 1);
1261             break;
1262     }
1263 
1264     return result;
1265 }
1266 
getDevicePropertyDesc(MtpDeviceProperty property)1267 MtpProperty* MtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
1268     JNIEnv* env = AndroidRuntime::getJNIEnv();
1269     MtpProperty* result = NULL;
1270     bool writable = false;
1271 
1272     // get current value
1273     jint ret = env->CallIntMethod(mDatabase, method_getDeviceProperty,
1274         (jint)property, mLongBuffer, mStringBuffer);
1275     if (ret == MTP_RESPONSE_OK) {
1276         switch (property) {
1277             case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
1278             case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
1279                 writable = true;
1280                 // fall through
1281                 FALLTHROUGH_INTENDED;
1282             case MTP_DEVICE_PROPERTY_IMAGE_SIZE:
1283             {
1284                 result = new MtpProperty(property, MTP_TYPE_STR, writable);
1285                 jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
1286                 result->setCurrentValue(str);
1287                 // for read-only properties it is safe to assume current value is default value
1288                 if (!writable)
1289                     result->setDefaultValue(str);
1290                 env->ReleaseCharArrayElements(mStringBuffer, str, 0);
1291                 break;
1292             }
1293             case MTP_DEVICE_PROPERTY_BATTERY_LEVEL:
1294             {
1295                 result = new MtpProperty(property, MTP_TYPE_UINT8);
1296                 jlong* arr = env->GetLongArrayElements(mLongBuffer, 0);
1297                 result->setFormRange(0, arr[1], 1);
1298                 result->mCurrentValue.u.u8 = (uint8_t) arr[0];
1299                 env->ReleaseLongArrayElements(mLongBuffer, arr, 0);
1300                 break;
1301             }
1302             case MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE:
1303             {
1304                 jlong* arr = env->GetLongArrayElements(mLongBuffer, 0);
1305                 result = new MtpProperty(property, MTP_TYPE_UINT32);
1306                 result->mCurrentValue.u.u32 = (uint32_t) arr[0];
1307                 env->ReleaseLongArrayElements(mLongBuffer, arr, 0);
1308                 break;
1309             }
1310             default:
1311                 ALOGE("Unrecognized property %x", property);
1312         }
1313     } else {
1314         ALOGE("unable to read device property, response: %04X", ret);
1315     }
1316 
1317     checkAndClearExceptionFromCallback(env, __FUNCTION__);
1318     return result;
1319 }
1320 
1321 // ----------------------------------------------------------------------------
1322 
1323 static void
android_mtp_MtpDatabase_setup(JNIEnv * env,jobject thiz)1324 android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz)
1325 {
1326     MtpDatabase* database = new MtpDatabase(env, thiz);
1327     env->SetLongField(thiz, field_context, (jlong)database);
1328     checkAndClearExceptionFromCallback(env, __FUNCTION__);
1329 }
1330 
1331 static void
android_mtp_MtpDatabase_finalize(JNIEnv * env,jobject thiz)1332 android_mtp_MtpDatabase_finalize(JNIEnv *env, jobject thiz)
1333 {
1334     MtpDatabase* database = (MtpDatabase *)env->GetLongField(thiz, field_context);
1335     database->cleanup(env);
1336     delete database;
1337     env->SetLongField(thiz, field_context, 0);
1338     checkAndClearExceptionFromCallback(env, __FUNCTION__);
1339 }
1340 
1341 static jstring
android_mtp_MtpPropertyGroup_format_date_time(JNIEnv * env,jobject,jlong seconds)1342 android_mtp_MtpPropertyGroup_format_date_time(JNIEnv *env, jobject /*thiz*/, jlong seconds)
1343 {
1344     char    date[20];
1345     formatDateTime(seconds, date, sizeof(date));
1346     return env->NewStringUTF(date);
1347 }
1348 
1349 // ----------------------------------------------------------------------------
1350 
1351 static const JNINativeMethod gMtpDatabaseMethods[] = {
1352     {"native_setup",            "()V",  (void *)android_mtp_MtpDatabase_setup},
1353     {"native_finalize",         "()V",  (void *)android_mtp_MtpDatabase_finalize},
1354 };
1355 
1356 static const JNINativeMethod gMtpPropertyGroupMethods[] = {
1357     {"format_date_time",        "(J)Ljava/lang/String;",
1358                                         (void *)android_mtp_MtpPropertyGroup_format_date_time},
1359 };
1360 
1361 #define GET_METHOD_ID(name, jclass, signature)                              \
1362     method_##name = env->GetMethodID(jclass, #name, signature);             \
1363     if (method_##name == NULL) {                                            \
1364         ALOGE("Can't find " #name);                                         \
1365         return -1;                                                          \
1366     }                                                                       \
1367 
register_android_mtp_MtpDatabase(JNIEnv * env)1368 int register_android_mtp_MtpDatabase(JNIEnv *env)
1369 {
1370     jclass clazz;
1371 
1372     clazz = env->FindClass("android/mtp/MtpDatabase");
1373     if (clazz == NULL) {
1374         ALOGE("Can't find android/mtp/MtpDatabase");
1375         return -1;
1376     }
1377     GET_METHOD_ID(beginSendObject, clazz, "(Ljava/lang/String;III)I");
1378     GET_METHOD_ID(endSendObject, clazz, "(IZ)V");
1379     GET_METHOD_ID(rescanFile, clazz, "(Ljava/lang/String;II)V");
1380     GET_METHOD_ID(getObjectList, clazz, "(III)[I");
1381     GET_METHOD_ID(getNumObjects, clazz, "(III)I");
1382     GET_METHOD_ID(getSupportedPlaybackFormats, clazz, "()[I");
1383     GET_METHOD_ID(getSupportedCaptureFormats, clazz, "()[I");
1384     GET_METHOD_ID(getSupportedObjectProperties, clazz, "(I)[I");
1385     GET_METHOD_ID(getSupportedDeviceProperties, clazz, "()[I");
1386     GET_METHOD_ID(setObjectProperty, clazz, "(IIJLjava/lang/String;)I");
1387     GET_METHOD_ID(getDeviceProperty, clazz, "(I[J[C)I");
1388     GET_METHOD_ID(setDeviceProperty, clazz, "(IJLjava/lang/String;)I");
1389     GET_METHOD_ID(getObjectPropertyList, clazz, "(IIIII)Landroid/mtp/MtpPropertyList;");
1390     GET_METHOD_ID(getObjectInfo, clazz, "(I[I[C[J)Z");
1391     GET_METHOD_ID(getObjectFilePath, clazz, "(I[C[J)I");
1392     GET_METHOD_ID(beginDeleteObject, clazz, "(I)I");
1393     GET_METHOD_ID(endDeleteObject, clazz, "(IZ)V");
1394     GET_METHOD_ID(beginMoveObject, clazz, "(III)I");
1395     GET_METHOD_ID(endMoveObject, clazz, "(IIIIIZ)V");
1396     GET_METHOD_ID(beginCopyObject, clazz, "(III)I");
1397     GET_METHOD_ID(endCopyObject, clazz, "(IZ)V");
1398     GET_METHOD_ID(getObjectReferences, clazz, "(I)[I");
1399     GET_METHOD_ID(setObjectReferences, clazz, "(I[I)I");
1400 
1401     field_context = env->GetFieldID(clazz, "mNativeContext", "J");
1402     if (field_context == NULL) {
1403         ALOGE("Can't find MtpDatabase.mNativeContext");
1404         return -1;
1405     }
1406 
1407     clazz = env->FindClass("android/mtp/MtpPropertyList");
1408     if (clazz == NULL) {
1409         ALOGE("Can't find android/mtp/MtpPropertyList");
1410         return -1;
1411     }
1412     GET_METHOD_ID(getCode, clazz, "()I");
1413     GET_METHOD_ID(getCount, clazz, "()I");
1414     GET_METHOD_ID(getObjectHandles, clazz, "()[I");
1415     GET_METHOD_ID(getPropertyCodes, clazz, "()[I");
1416     GET_METHOD_ID(getDataTypes, clazz, "()[I");
1417     GET_METHOD_ID(getLongValues, clazz, "()[J");
1418     GET_METHOD_ID(getStringValues, clazz, "()[Ljava/lang/String;");
1419 
1420     if (AndroidRuntime::registerNativeMethods(env,
1421                 "android/mtp/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods)))
1422         return -1;
1423 
1424     return AndroidRuntime::registerNativeMethods(env,
1425                 "android/mtp/MtpPropertyGroup", gMtpPropertyGroupMethods, NELEM(gMtpPropertyGroupMethods));
1426 }
1427