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