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