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