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