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