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