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