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