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