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