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_NDEBUG 0
18
19 #define LOG_TAG "MtpDeviceJNI"
20 #include "utils/Log.h"
21
22 #include <stdio.h>
23 #include <assert.h>
24 #include <limits.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27
28 #include <memory>
29 #include <string>
30
31 #include "jni.h"
32 #include <nativehelper/JNIHelp.h>
33 #include <nativehelper/ScopedPrimitiveArray.h>
34
35 #include "android_runtime/AndroidRuntime.h"
36 #include "android_runtime/Log.h"
37 #include "core_jni_helpers.h"
38 #include "nativehelper/ScopedLocalRef.h"
39 #include "private/android_filesystem_config.h"
40
41 #include "MtpTypes.h"
42 #include "MtpDevice.h"
43 #include "MtpDeviceInfo.h"
44 #include "MtpStorageInfo.h"
45 #include "MtpObjectInfo.h"
46 #include "MtpProperty.h"
47
48 using namespace android;
49
50 // ----------------------------------------------------------------------------
51
52 namespace {
53
54 static jfieldID field_context;
55
56 jclass clazz_deviceInfo;
57 jclass clazz_storageInfo;
58 jclass clazz_objectInfo;
59 jclass clazz_event;
60 jclass clazz_io_exception;
61 jclass clazz_operation_canceled_exception;
62
63 jmethodID constructor_deviceInfo;
64 jmethodID constructor_storageInfo;
65 jmethodID constructor_objectInfo;
66 jmethodID constructor_event;
67
68 // MtpDeviceInfo fields
69 static jfieldID field_deviceInfo_manufacturer;
70 static jfieldID field_deviceInfo_model;
71 static jfieldID field_deviceInfo_version;
72 static jfieldID field_deviceInfo_serialNumber;
73 static jfieldID field_deviceInfo_operationsSupported;
74 static jfieldID field_deviceInfo_eventsSupported;
75 static jfieldID field_deviceInfo_devicePropertySupported;
76
77 // MtpStorageInfo fields
78 static jfieldID field_storageInfo_storageId;
79 static jfieldID field_storageInfo_maxCapacity;
80 static jfieldID field_storageInfo_freeSpace;
81 static jfieldID field_storageInfo_description;
82 static jfieldID field_storageInfo_volumeIdentifier;
83
84 // MtpObjectInfo fields
85 static jfieldID field_objectInfo_handle;
86 static jfieldID field_objectInfo_storageId;
87 static jfieldID field_objectInfo_format;
88 static jfieldID field_objectInfo_protectionStatus;
89 static jfieldID field_objectInfo_compressedSize;
90 static jfieldID field_objectInfo_thumbFormat;
91 static jfieldID field_objectInfo_thumbCompressedSize;
92 static jfieldID field_objectInfo_thumbPixWidth;
93 static jfieldID field_objectInfo_thumbPixHeight;
94 static jfieldID field_objectInfo_imagePixWidth;
95 static jfieldID field_objectInfo_imagePixHeight;
96 static jfieldID field_objectInfo_imagePixDepth;
97 static jfieldID field_objectInfo_parent;
98 static jfieldID field_objectInfo_associationType;
99 static jfieldID field_objectInfo_associationDesc;
100 static jfieldID field_objectInfo_sequenceNumber;
101 static jfieldID field_objectInfo_name;
102 static jfieldID field_objectInfo_dateCreated;
103 static jfieldID field_objectInfo_dateModified;
104 static jfieldID field_objectInfo_keywords;
105
106 // MtpEvent fields
107 static jfieldID field_event_eventCode;
108 static jfieldID field_event_parameter1;
109 static jfieldID field_event_parameter2;
110 static jfieldID field_event_parameter3;
111
112 // Initializer for the jclasses, jfieldIDs and jmethodIDs declared above. This method must be
113 // invoked before using these static fields for JNI accesses.
initializeJavaIDs(JNIEnv * env)114 static void initializeJavaIDs(JNIEnv* env) {
115 static std::once_flag sJniInitialized;
116
117 std::call_once(sJniInitialized, [](JNIEnv* env) {
118 clazz_deviceInfo =
119 (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpDeviceInfo"));
120 constructor_deviceInfo = GetMethodIDOrDie(env, clazz_deviceInfo, "<init>", "()V");
121 field_deviceInfo_manufacturer =
122 GetFieldIDOrDie(env, clazz_deviceInfo, "mManufacturer", "Ljava/lang/String;");
123 field_deviceInfo_model =
124 GetFieldIDOrDie(env, clazz_deviceInfo, "mModel", "Ljava/lang/String;");
125 field_deviceInfo_version =
126 GetFieldIDOrDie(env, clazz_deviceInfo, "mVersion", "Ljava/lang/String;");
127 field_deviceInfo_serialNumber =
128 GetFieldIDOrDie(env, clazz_deviceInfo, "mSerialNumber", "Ljava/lang/String;");
129 field_deviceInfo_operationsSupported =
130 GetFieldIDOrDie(env, clazz_deviceInfo, "mOperationsSupported", "[I");
131 field_deviceInfo_eventsSupported =
132 GetFieldIDOrDie(env, clazz_deviceInfo, "mEventsSupported", "[I");
133 field_deviceInfo_devicePropertySupported =
134 GetFieldIDOrDie(env, clazz_deviceInfo, "mDevicePropertySupported", "[I");
135
136 clazz_storageInfo =
137 (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpStorageInfo"));
138 constructor_storageInfo = GetMethodIDOrDie(env, clazz_storageInfo, "<init>", "()V");
139 field_storageInfo_storageId = GetFieldIDOrDie(env, clazz_storageInfo, "mStorageId", "I");
140 field_storageInfo_maxCapacity =
141 GetFieldIDOrDie(env, clazz_storageInfo, "mMaxCapacity", "J");
142 field_storageInfo_freeSpace =
143 GetFieldIDOrDie(env, clazz_storageInfo, "mFreeSpace", "J");
144 field_storageInfo_description =
145 GetFieldIDOrDie(env, clazz_storageInfo, "mDescription", "Ljava/lang/String;");
146 field_storageInfo_volumeIdentifier =
147 GetFieldIDOrDie(env, clazz_storageInfo, "mVolumeIdentifier", "Ljava/lang/String;");
148
149 clazz_objectInfo =
150 (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpObjectInfo"));
151 constructor_objectInfo = GetMethodIDOrDie(env, clazz_objectInfo, "<init>", "()V");
152 field_objectInfo_handle = GetFieldIDOrDie(env, clazz_objectInfo, "mHandle", "I");
153 field_objectInfo_storageId = GetFieldIDOrDie(env, clazz_objectInfo, "mStorageId", "I");
154 field_objectInfo_format = GetFieldIDOrDie(env, clazz_objectInfo, "mFormat", "I");
155 field_objectInfo_protectionStatus =
156 GetFieldIDOrDie(env, clazz_objectInfo, "mProtectionStatus", "I");
157 field_objectInfo_compressedSize =
158 GetFieldIDOrDie(env, clazz_objectInfo, "mCompressedSize", "I");
159 field_objectInfo_thumbFormat = GetFieldIDOrDie(env, clazz_objectInfo, "mThumbFormat", "I");
160 field_objectInfo_thumbCompressedSize =
161 GetFieldIDOrDie(env, clazz_objectInfo, "mThumbCompressedSize", "I");
162 field_objectInfo_thumbPixWidth =
163 GetFieldIDOrDie(env, clazz_objectInfo, "mThumbPixWidth", "I");
164 field_objectInfo_thumbPixHeight =
165 GetFieldIDOrDie(env, clazz_objectInfo, "mThumbPixHeight", "I");
166 field_objectInfo_imagePixWidth =
167 GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixWidth", "I");
168 field_objectInfo_imagePixHeight =
169 GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixHeight", "I");
170 field_objectInfo_imagePixDepth =
171 GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixDepth", "I");
172 field_objectInfo_parent = GetFieldIDOrDie(env, clazz_objectInfo, "mParent", "I");
173 field_objectInfo_associationType =
174 GetFieldIDOrDie(env, clazz_objectInfo, "mAssociationType", "I");
175 field_objectInfo_associationDesc =
176 GetFieldIDOrDie(env, clazz_objectInfo, "mAssociationDesc", "I");
177 field_objectInfo_sequenceNumber =
178 GetFieldIDOrDie(env, clazz_objectInfo, "mSequenceNumber", "I");
179 field_objectInfo_name =
180 GetFieldIDOrDie(env, clazz_objectInfo, "mName", "Ljava/lang/String;");
181 field_objectInfo_dateCreated = GetFieldIDOrDie(env, clazz_objectInfo, "mDateCreated", "J");
182 field_objectInfo_dateModified =
183 GetFieldIDOrDie(env, clazz_objectInfo, "mDateModified", "J");
184 field_objectInfo_keywords =
185 GetFieldIDOrDie(env, clazz_objectInfo, "mKeywords", "Ljava/lang/String;");
186
187 clazz_event = (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpEvent"));
188 constructor_event = GetMethodIDOrDie(env, clazz_event, "<init>", "()V");
189 field_event_eventCode = GetFieldIDOrDie(env, clazz_event, "mEventCode", "I");
190 field_event_parameter1 = GetFieldIDOrDie(env, clazz_event, "mParameter1", "I");
191 field_event_parameter2 = GetFieldIDOrDie(env, clazz_event, "mParameter2", "I");
192 field_event_parameter3 = GetFieldIDOrDie(env, clazz_event, "mParameter3", "I");
193
194 const jclass clazz = FindClassOrDie(env, "android/mtp/MtpDevice");
195 field_context = GetFieldIDOrDie(env, clazz, "mNativeContext", "J");
196
197 clazz_io_exception = (jclass)env->NewGlobalRef(FindClassOrDie(env, "java/io/IOException"));
198 clazz_operation_canceled_exception =
199 (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/os/OperationCanceledException"));
200 }, env);
201 }
202
203 class JavaArrayWriter {
204 public:
JavaArrayWriter(JNIEnv * env,jbyteArray array)205 JavaArrayWriter(JNIEnv* env, jbyteArray array) :
206 mEnv(env), mArray(array), mSize(mEnv->GetArrayLength(mArray)) {}
write(void * data,uint32_t offset,uint32_t length)207 bool write(void* data, uint32_t offset, uint32_t length) {
208 if (static_cast<uint32_t>(mSize) < offset + length) {
209 return false;
210 }
211 mEnv->SetByteArrayRegion(mArray, offset, length, static_cast<jbyte*>(data));
212 return true;
213 }
writeTo(void * data,uint32_t offset,uint32_t length,void * clientData)214 static bool writeTo(void* data, uint32_t offset, uint32_t length, void* clientData) {
215 return static_cast<JavaArrayWriter*>(clientData)->write(data, offset, length);
216 }
217
218 private:
219 JNIEnv* mEnv;
220 jbyteArray mArray;
221 jsize mSize;
222 };
223
224 }
225
get_device_from_object(JNIEnv * env,jobject javaDevice)226 MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice)
227 {
228 // get_device_from_object() is called by the majority of JNI methods in this file. Call
229 // `initializeJavaIDs()` here to ensure JNI methodID's, fieldIDs and classes are initialized
230 // before use.
231 initializeJavaIDs(env);
232
233 return (MtpDevice*)env->GetLongField(javaDevice, field_context);
234 }
235
fill_jobject_from_object_info(JNIEnv * env,jobject object,MtpObjectInfo * objectInfo)236 void fill_jobject_from_object_info(JNIEnv* env, jobject object, MtpObjectInfo* objectInfo) {
237 if (objectInfo->mHandle)
238 env->SetIntField(object, field_objectInfo_handle, objectInfo->mHandle);
239 if (objectInfo->mStorageID)
240 env->SetIntField(object, field_objectInfo_storageId, objectInfo->mStorageID);
241 if (objectInfo->mFormat)
242 env->SetIntField(object, field_objectInfo_format, objectInfo->mFormat);
243 if (objectInfo->mProtectionStatus)
244 env->SetIntField(object, field_objectInfo_protectionStatus, objectInfo->mProtectionStatus);
245 if (objectInfo->mCompressedSize)
246 env->SetIntField(object, field_objectInfo_compressedSize, objectInfo->mCompressedSize);
247 if (objectInfo->mThumbFormat)
248 env->SetIntField(object, field_objectInfo_thumbFormat, objectInfo->mThumbFormat);
249 if (objectInfo->mThumbCompressedSize) {
250 env->SetIntField(object, field_objectInfo_thumbCompressedSize,
251 objectInfo->mThumbCompressedSize);
252 }
253 if (objectInfo->mThumbPixWidth)
254 env->SetIntField(object, field_objectInfo_thumbPixWidth, objectInfo->mThumbPixWidth);
255 if (objectInfo->mThumbPixHeight)
256 env->SetIntField(object, field_objectInfo_thumbPixHeight, objectInfo->mThumbPixHeight);
257 if (objectInfo->mImagePixWidth)
258 env->SetIntField(object, field_objectInfo_imagePixWidth, objectInfo->mImagePixWidth);
259 if (objectInfo->mImagePixHeight)
260 env->SetIntField(object, field_objectInfo_imagePixHeight, objectInfo->mImagePixHeight);
261 if (objectInfo->mImagePixDepth)
262 env->SetIntField(object, field_objectInfo_imagePixDepth, objectInfo->mImagePixDepth);
263 if (objectInfo->mParent)
264 env->SetIntField(object, field_objectInfo_parent, objectInfo->mParent);
265 if (objectInfo->mAssociationType)
266 env->SetIntField(object, field_objectInfo_associationType, objectInfo->mAssociationType);
267 if (objectInfo->mAssociationDesc)
268 env->SetIntField(object, field_objectInfo_associationDesc, objectInfo->mAssociationDesc);
269 if (objectInfo->mSequenceNumber)
270 env->SetIntField(object, field_objectInfo_sequenceNumber, objectInfo->mSequenceNumber);
271 if (objectInfo->mName)
272 env->SetObjectField(object, field_objectInfo_name, env->NewStringUTF(objectInfo->mName));
273 if (objectInfo->mDateCreated)
274 env->SetLongField(object, field_objectInfo_dateCreated, objectInfo->mDateCreated * 1000LL);
275 if (objectInfo->mDateModified) {
276 env->SetLongField(object, field_objectInfo_dateModified,
277 objectInfo->mDateModified * 1000LL);
278 }
279 if (objectInfo->mKeywords) {
280 env->SetObjectField(object, field_objectInfo_keywords,
281 env->NewStringUTF(objectInfo->mKeywords));
282 }
283 }
284
285 // ----------------------------------------------------------------------------
286
287 static jboolean
android_mtp_MtpDevice_open(JNIEnv * env,jobject thiz,jstring deviceName,jint fd)288 android_mtp_MtpDevice_open(JNIEnv *env, jobject thiz, jstring deviceName, jint fd)
289 {
290 const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
291 if (deviceNameStr == NULL) {
292 return JNI_FALSE;
293 }
294
295 // The passed in fd is maintained by the UsbDeviceConnection
296 fd = dup(fd);
297
298 MtpDevice* device = MtpDevice::open(deviceNameStr, fd);
299 env->ReleaseStringUTFChars(deviceName, deviceNameStr);
300
301 // The jfieldID `field_context` needs to be initialized before use below.
302 initializeJavaIDs(env);
303 if (device)
304 env->SetLongField(thiz, field_context, (jlong)device);
305 return (jboolean)(device != NULL);
306 }
307
308 static void
android_mtp_MtpDevice_close(JNIEnv * env,jobject thiz)309 android_mtp_MtpDevice_close(JNIEnv *env, jobject thiz)
310 {
311 MtpDevice* device = get_device_from_object(env, thiz);
312 if (device) {
313 device->close();
314 delete device;
315 env->SetLongField(thiz, field_context, 0);
316 }
317 }
318
319 static jobject
android_mtp_MtpDevice_get_device_info(JNIEnv * env,jobject thiz)320 android_mtp_MtpDevice_get_device_info(JNIEnv *env, jobject thiz)
321 {
322 MtpDevice* device = get_device_from_object(env, thiz);
323 if (!device) {
324 ALOGD("android_mtp_MtpDevice_get_device_info device is null");
325 return NULL;
326 }
327 std::unique_ptr<MtpDeviceInfo> deviceInfo(device->getDeviceInfo());
328 if (!deviceInfo) {
329 ALOGD("android_mtp_MtpDevice_get_device_info deviceInfo is null");
330 return NULL;
331 }
332 jobject info = env->NewObject(clazz_deviceInfo, constructor_deviceInfo);
333 if (info == NULL) {
334 ALOGE("Could not create a MtpDeviceInfo object");
335 return NULL;
336 }
337
338 if (deviceInfo->mManufacturer)
339 env->SetObjectField(info, field_deviceInfo_manufacturer,
340 env->NewStringUTF(deviceInfo->mManufacturer));
341 if (deviceInfo->mModel)
342 env->SetObjectField(info, field_deviceInfo_model,
343 env->NewStringUTF(deviceInfo->mModel));
344 if (deviceInfo->mVersion)
345 env->SetObjectField(info, field_deviceInfo_version,
346 env->NewStringUTF(deviceInfo->mVersion));
347 if (deviceInfo->mSerial)
348 env->SetObjectField(info, field_deviceInfo_serialNumber,
349 env->NewStringUTF(deviceInfo->mSerial));
350 assert(deviceInfo->mOperations);
351 {
352 const size_t size = deviceInfo->mOperations->size();
353 ScopedLocalRef<jintArray> operations(env, static_cast<jintArray>(env->NewIntArray(size)));
354 {
355 ScopedIntArrayRW elements(env, operations.get());
356 if (elements.get() == NULL) {
357 ALOGE("Could not create operationsSupported element.");
358 return NULL;
359 }
360 for (size_t i = 0; i < size; ++i) {
361 elements[i] = static_cast<int>(deviceInfo->mOperations->at(i));
362 }
363 env->SetObjectField(info, field_deviceInfo_operationsSupported, operations.get());
364 }
365 }
366 assert(deviceInfo->mEvents);
367 {
368 const size_t size = deviceInfo->mEvents->size();
369 ScopedLocalRef<jintArray> events(env, static_cast<jintArray>(env->NewIntArray(size)));
370 {
371 ScopedIntArrayRW elements(env, events.get());
372 if (elements.get() == NULL) {
373 ALOGE("Could not create eventsSupported element.");
374 return NULL;
375 }
376 for (size_t i = 0; i < size; ++i) {
377 elements[i] = static_cast<int>(deviceInfo->mEvents->at(i));
378 }
379 env->SetObjectField(info, field_deviceInfo_eventsSupported, events.get());
380 }
381 }
382
383 assert(deviceInfo->mDeviceProperties);
384 {
385 const size_t size = deviceInfo->mDeviceProperties->size();
386 ScopedLocalRef<jintArray> events(env, static_cast<jintArray>(env->NewIntArray(size)));
387 {
388 ScopedIntArrayRW elements(env, events.get());
389 if (elements.get() == NULL) {
390 ALOGE("Could not create devicePropertySupported element.");
391 return NULL;
392 }
393 for (size_t i = 0; i < size; ++i) {
394 elements[i] = static_cast<int>(deviceInfo->mDeviceProperties->at(i));
395 }
396 env->SetObjectField(info, field_deviceInfo_devicePropertySupported, events.get());
397 }
398 }
399
400 return info;
401 }
402
403 static jint
android_mtp_MtpDevice_set_device_property_init_version(JNIEnv * env,jobject thiz,jstring property_str)404 android_mtp_MtpDevice_set_device_property_init_version(JNIEnv *env, jobject thiz,
405 jstring property_str) {
406 MtpDevice* const device = get_device_from_object(env, thiz);
407
408 if (!device) {
409 ALOGD("%s device is null\n", __func__);
410 env->ThrowNew(clazz_io_exception, "Failed to obtain MtpDevice.");
411 return -1;
412 }
413
414 const char *propertyStr = env->GetStringUTFChars(property_str, NULL);
415 if (propertyStr == NULL) {
416 return -1;
417 }
418
419 MtpProperty property(MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO, MTP_TYPE_STR, true);
420 if (property.getDataType() != MTP_TYPE_STR) {
421 env->ThrowNew(clazz_io_exception, "Unexpected property data type.");
422 return -1;
423 }
424
425 property.setCurrentValue(propertyStr);
426 if (!device->setDevicePropValueStr(&property)) {
427 env->ThrowNew(clazz_io_exception, "Failed to obtain property value.");
428 return -1;
429 }
430
431 env->ReleaseStringUTFChars(property_str, propertyStr);
432
433 return 0;
434 }
435
436 static jintArray
android_mtp_MtpDevice_get_storage_ids(JNIEnv * env,jobject thiz)437 android_mtp_MtpDevice_get_storage_ids(JNIEnv *env, jobject thiz)
438 {
439 MtpDevice* device = get_device_from_object(env, thiz);
440 if (!device)
441 return NULL;
442 MtpStorageIDList* storageIDs = device->getStorageIDs();
443 if (!storageIDs)
444 return NULL;
445
446 int length = storageIDs->size();
447 jintArray array = env->NewIntArray(length);
448 // FIXME is this cast safe?
449 env->SetIntArrayRegion(array, 0, length, (const jint *)storageIDs->data());
450
451 delete storageIDs;
452 return array;
453 }
454
455 static jobject
android_mtp_MtpDevice_get_storage_info(JNIEnv * env,jobject thiz,jint storageID)456 android_mtp_MtpDevice_get_storage_info(JNIEnv *env, jobject thiz, jint storageID)
457 {
458 MtpDevice* device = get_device_from_object(env, thiz);
459 if (!device)
460 return NULL;
461 MtpStorageInfo* storageInfo = device->getStorageInfo(storageID);
462 if (!storageInfo)
463 return NULL;
464
465 jobject info = env->NewObject(clazz_storageInfo, constructor_storageInfo);
466 if (info == NULL) {
467 ALOGE("Could not create a MtpStorageInfo object");
468 delete storageInfo;
469 return NULL;
470 }
471
472 if (storageInfo->mStorageID)
473 env->SetIntField(info, field_storageInfo_storageId, storageInfo->mStorageID);
474 if (storageInfo->mMaxCapacity)
475 env->SetLongField(info, field_storageInfo_maxCapacity, storageInfo->mMaxCapacity);
476 if (storageInfo->mFreeSpaceBytes)
477 env->SetLongField(info, field_storageInfo_freeSpace, storageInfo->mFreeSpaceBytes);
478 if (storageInfo->mStorageDescription)
479 env->SetObjectField(info, field_storageInfo_description,
480 env->NewStringUTF(storageInfo->mStorageDescription));
481 if (storageInfo->mVolumeIdentifier)
482 env->SetObjectField(info, field_storageInfo_volumeIdentifier,
483 env->NewStringUTF(storageInfo->mVolumeIdentifier));
484
485 delete storageInfo;
486 return info;
487 }
488
489 static jintArray
android_mtp_MtpDevice_get_object_handles(JNIEnv * env,jobject thiz,jint storageID,jint format,jint objectID)490 android_mtp_MtpDevice_get_object_handles(JNIEnv *env, jobject thiz,
491 jint storageID, jint format, jint objectID)
492 {
493 MtpDevice* device = get_device_from_object(env, thiz);
494 if (!device)
495 return NULL;
496 MtpObjectHandleList* handles = device->getObjectHandles(storageID, format, objectID);
497 if (!handles)
498 return NULL;
499
500 int length = handles->size();
501 jintArray array = env->NewIntArray(length);
502 // FIXME is this cast safe?
503 env->SetIntArrayRegion(array, 0, length, (const jint *)handles->data());
504
505 delete handles;
506 return array;
507 }
508
509 static jobject
android_mtp_MtpDevice_get_object_info(JNIEnv * env,jobject thiz,jint objectID)510 android_mtp_MtpDevice_get_object_info(JNIEnv *env, jobject thiz, jint objectID)
511 {
512 MtpDevice* device = get_device_from_object(env, thiz);
513 if (!device)
514 return NULL;
515 MtpObjectInfo* objectInfo = device->getObjectInfo(objectID);
516 if (!objectInfo)
517 return NULL;
518 jobject info = env->NewObject(clazz_objectInfo, constructor_objectInfo);
519 if (info == NULL) {
520 ALOGE("Could not create a MtpObjectInfo object");
521 delete objectInfo;
522 return NULL;
523 }
524
525 fill_jobject_from_object_info(env, info, objectInfo);
526 delete objectInfo;
527 return info;
528 }
529
check_uint32_arg(JNIEnv * env,const char * name,jlong value,uint32_t * out)530 bool check_uint32_arg(JNIEnv *env, const char* name, jlong value, uint32_t* out) {
531 if (value < 0 || 0xffffffff < value) {
532 jniThrowException(
533 env,
534 "java/lang/IllegalArgumentException",
535 (std::string("argument must be a 32-bit unsigned integer: ") + name).c_str());
536 return false;
537 }
538 *out = static_cast<uint32_t>(value);
539 return true;
540 }
541
542 static jbyteArray
android_mtp_MtpDevice_get_object(JNIEnv * env,jobject thiz,jint objectID,jlong objectSizeLong)543 android_mtp_MtpDevice_get_object(JNIEnv *env, jobject thiz, jint objectID, jlong objectSizeLong)
544 {
545 uint32_t objectSize;
546 if (!check_uint32_arg(env, "objectSize", objectSizeLong, &objectSize)) {
547 return nullptr;
548 }
549
550 MtpDevice* device = get_device_from_object(env, thiz);
551 if (!device) {
552 return nullptr;
553 }
554
555 ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(objectSize));
556 if (!array.get()) {
557 jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
558 return nullptr;
559 }
560
561 JavaArrayWriter writer(env, array.get());
562
563 if (device->readObject(objectID, JavaArrayWriter::writeTo, objectSize, &writer)) {
564 return array.release();
565 }
566 return nullptr;
567 }
568
569 static jlong
android_mtp_MtpDevice_get_partial_object(JNIEnv * env,jobject thiz,jint objectID,jlong offsetLong,jlong sizeLong,jbyteArray array)570 android_mtp_MtpDevice_get_partial_object(JNIEnv *env,
571 jobject thiz,
572 jint objectID,
573 jlong offsetLong,
574 jlong sizeLong,
575 jbyteArray array)
576 {
577 if (!array) {
578 jniThrowException(env, "java/lang/IllegalArgumentException", "Array must not be null.");
579 return -1;
580 }
581
582 uint32_t offset;
583 uint32_t size;
584 if (!check_uint32_arg(env, "offset", offsetLong, &offset) ||
585 !check_uint32_arg(env, "size", sizeLong, &size)) {
586 return -1;
587 }
588
589 MtpDevice* const device = get_device_from_object(env, thiz);
590 if (!device) {
591 jniThrowException(env, "java/io/IOException", "Failed to obtain MtpDevice.");
592 return -1;
593 }
594
595 JavaArrayWriter writer(env, array);
596 uint32_t written_size;
597 const bool success = device->readPartialObject(
598 objectID, offset, size, &written_size, JavaArrayWriter::writeTo, &writer);
599 if (!success) {
600 jniThrowException(env, "java/io/IOException", "Failed to read data.");
601 return -1;
602 }
603 return static_cast<jlong>(written_size);
604 }
605
606 static jint
android_mtp_MtpDevice_get_partial_object_64(JNIEnv * env,jobject thiz,jint objectID,jlong offset,jlong size,jbyteArray array)607 android_mtp_MtpDevice_get_partial_object_64(JNIEnv *env,
608 jobject thiz,
609 jint objectID,
610 jlong offset,
611 jlong size,
612 jbyteArray array) {
613 if (!array) {
614 jniThrowException(env, "java/lang/IllegalArgumentException", "Array must not be null.");
615 return -1;
616 }
617
618 if (offset < 0) {
619 jniThrowException(
620 env,
621 "java/lang/IllegalArgumentException",
622 "Offset argument must not be a negative value.");
623 return -1;
624 }
625
626 if (size < 0 || 0xffffffffL < size) {
627 jniThrowException(
628 env,
629 "java/lang/IllegalArgumentException",
630 "Size argument must be a 32-bit unsigned integer.");
631 return -1;
632 }
633
634 MtpDevice* const device = get_device_from_object(env, thiz);
635 if (!device) {
636 jniThrowException(env, "java/io/IOException", "Failed to obtain MtpDevice.");
637 return -1;
638 }
639
640 const uint32_t native_object_handle = static_cast<uint32_t>(objectID);
641 const uint64_t native_offset = static_cast<uint64_t>(offset);
642 const uint32_t native_size = static_cast<uint32_t>(size);
643
644 JavaArrayWriter writer(env, array);
645 uint32_t written_size;
646 const bool success = device->readPartialObject64(
647 native_object_handle,
648 native_offset,
649 native_size,
650 &written_size,
651 JavaArrayWriter::writeTo,
652 &writer);
653 if (!success) {
654 jniThrowException(env, "java/io/IOException", "Failed to read data.");
655 return -1;
656 }
657 return static_cast<jint>(written_size);
658 }
659
660 static jbyteArray
android_mtp_MtpDevice_get_thumbnail(JNIEnv * env,jobject thiz,jint objectID)661 android_mtp_MtpDevice_get_thumbnail(JNIEnv *env, jobject thiz, jint objectID)
662 {
663 MtpDevice* device = get_device_from_object(env, thiz);
664 if (!device)
665 return NULL;
666
667 int length;
668 void* thumbnail = device->getThumbnail(objectID, length);
669 if (! thumbnail)
670 return NULL;
671 jbyteArray array = env->NewByteArray(length);
672 env->SetByteArrayRegion(array, 0, length, (const jbyte *)thumbnail);
673
674 free(thumbnail);
675 return array;
676 }
677
678 static jboolean
android_mtp_MtpDevice_delete_object(JNIEnv * env,jobject thiz,jint object_id)679 android_mtp_MtpDevice_delete_object(JNIEnv *env, jobject thiz, jint object_id)
680 {
681 MtpDevice* device = get_device_from_object(env, thiz);
682 if (device && device->deleteObject(object_id)) {
683 return JNI_TRUE;
684 } else {
685 return JNI_FALSE;
686 }
687 }
688
689 static jint
android_mtp_MtpDevice_get_parent(JNIEnv * env,jobject thiz,jint object_id)690 android_mtp_MtpDevice_get_parent(JNIEnv *env, jobject thiz, jint object_id)
691 {
692 MtpDevice* device = get_device_from_object(env, thiz);
693 if (device)
694 return static_cast<jint>(device->getParent(object_id));
695 else
696 return -1;
697 }
698
699 static jint
android_mtp_MtpDevice_get_storage_id(JNIEnv * env,jobject thiz,jint object_id)700 android_mtp_MtpDevice_get_storage_id(JNIEnv *env, jobject thiz, jint object_id)
701 {
702 MtpDevice* device = get_device_from_object(env, thiz);
703 if (device)
704 return static_cast<jint>(device->getStorageID(object_id));
705 else
706 return -1;
707 }
708
709 static jboolean
android_mtp_MtpDevice_import_file(JNIEnv * env,jobject thiz,jint object_id,jstring dest_path)710 android_mtp_MtpDevice_import_file(JNIEnv *env, jobject thiz, jint object_id, jstring dest_path)
711 {
712 MtpDevice* device = get_device_from_object(env, thiz);
713 if (device) {
714 const char *destPathStr = env->GetStringUTFChars(dest_path, NULL);
715 if (destPathStr == NULL) {
716 return JNI_FALSE;
717 }
718
719 jboolean result = device->readObject(object_id, destPathStr, AID_SDCARD_RW, 0664);
720 env->ReleaseStringUTFChars(dest_path, destPathStr);
721 return result;
722 }
723
724 return JNI_FALSE;
725 }
726
727 static jboolean
android_mtp_MtpDevice_import_file_to_fd(JNIEnv * env,jobject thiz,jint object_id,jint fd)728 android_mtp_MtpDevice_import_file_to_fd(JNIEnv *env, jobject thiz, jint object_id, jint fd)
729 {
730 MtpDevice* device = get_device_from_object(env, thiz);
731 if (device)
732 return device->readObject(object_id, fd);
733 else
734 return JNI_FALSE;
735 }
736
737 static jboolean
android_mtp_MtpDevice_send_object(JNIEnv * env,jobject thiz,jint object_id,jlong sizeLong,jint fd)738 android_mtp_MtpDevice_send_object(
739 JNIEnv *env, jobject thiz, jint object_id, jlong sizeLong, jint fd)
740 {
741 uint32_t size;
742 if (!check_uint32_arg(env, "size", sizeLong, &size))
743 return JNI_FALSE;
744
745 MtpDevice* device = get_device_from_object(env, thiz);
746 if (!device)
747 return JNI_FALSE;
748
749 return device->sendObject(object_id, size, fd);
750 }
751
752 static jobject
android_mtp_MtpDevice_send_object_info(JNIEnv * env,jobject thiz,jobject info)753 android_mtp_MtpDevice_send_object_info(JNIEnv *env, jobject thiz, jobject info)
754 {
755 MtpDevice* device = get_device_from_object(env, thiz);
756 if (!device) {
757 return NULL;
758 }
759
760 // Updating existing objects is not supported.
761 if (env->GetIntField(info, field_objectInfo_handle) != -1) {
762 return NULL;
763 }
764
765 MtpObjectInfo* object_info = new MtpObjectInfo(-1);
766 object_info->mStorageID = env->GetIntField(info, field_objectInfo_storageId);
767 object_info->mFormat = env->GetIntField(info, field_objectInfo_format);
768 object_info->mProtectionStatus = env->GetIntField(info, field_objectInfo_protectionStatus);
769 object_info->mCompressedSize = env->GetIntField(info, field_objectInfo_compressedSize);
770 object_info->mThumbFormat = env->GetIntField(info, field_objectInfo_thumbFormat);
771 object_info->mThumbCompressedSize =
772 env->GetIntField(info, field_objectInfo_thumbCompressedSize);
773 object_info->mThumbPixWidth = env->GetIntField(info, field_objectInfo_thumbPixWidth);
774 object_info->mThumbPixHeight = env->GetIntField(info, field_objectInfo_thumbPixHeight);
775 object_info->mImagePixWidth = env->GetIntField(info, field_objectInfo_imagePixWidth);
776 object_info->mImagePixHeight = env->GetIntField(info, field_objectInfo_imagePixHeight);
777 object_info->mImagePixDepth = env->GetIntField(info, field_objectInfo_imagePixDepth);
778 object_info->mParent = env->GetIntField(info, field_objectInfo_parent);
779 object_info->mAssociationType = env->GetIntField(info, field_objectInfo_associationType);
780 object_info->mAssociationDesc = env->GetIntField(info, field_objectInfo_associationDesc);
781 object_info->mSequenceNumber = env->GetIntField(info, field_objectInfo_sequenceNumber);
782
783 jstring name_jstring = (jstring) env->GetObjectField(info, field_objectInfo_name);
784 if (name_jstring != NULL) {
785 const char* name_string = env->GetStringUTFChars(name_jstring, NULL);
786 object_info->mName = strdup(name_string);
787 env->ReleaseStringUTFChars(name_jstring, name_string);
788 }
789
790 object_info->mDateCreated = env->GetLongField(info, field_objectInfo_dateCreated) / 1000LL;
791 object_info->mDateModified = env->GetLongField(info, field_objectInfo_dateModified) / 1000LL;
792
793 jstring keywords_jstring = (jstring) env->GetObjectField(info, field_objectInfo_keywords);
794 if (keywords_jstring != NULL) {
795 const char* keywords_string = env->GetStringUTFChars(keywords_jstring, NULL);
796 object_info->mKeywords = strdup(keywords_string);
797 env->ReleaseStringUTFChars(keywords_jstring, keywords_string);
798 }
799
800 int object_handle = device->sendObjectInfo(object_info);
801 if (object_handle == -1) {
802 delete object_info;
803 return NULL;
804 }
805
806 object_info->mHandle = object_handle;
807 jobject result = env->NewObject(clazz_objectInfo, constructor_objectInfo);
808 if (result == NULL) {
809 ALOGE("Could not create a MtpObjectInfo object");
810 delete object_info;
811 return NULL;
812 }
813
814 fill_jobject_from_object_info(env, result, object_info);
815 delete object_info;
816 return result;
817 }
818
android_mtp_MtpDevice_submit_event_request(JNIEnv * env,jobject thiz)819 static jint android_mtp_MtpDevice_submit_event_request(JNIEnv *env, jobject thiz)
820 {
821 MtpDevice* const device = get_device_from_object(env, thiz);
822 if (!device) {
823 env->ThrowNew(clazz_io_exception, "");
824 return -1;
825 }
826 return device->submitEventRequest();
827 }
828
android_mtp_MtpDevice_reap_event_request(JNIEnv * env,jobject thiz,jint seq)829 static jobject android_mtp_MtpDevice_reap_event_request(JNIEnv *env, jobject thiz, jint seq)
830 {
831 MtpDevice* const device = get_device_from_object(env, thiz);
832 if (!device) {
833 env->ThrowNew(clazz_io_exception, "");
834 return NULL;
835 }
836 uint32_t parameters[3];
837 const int eventCode = device->reapEventRequest(seq, ¶meters);
838 if (eventCode <= 0) {
839 env->ThrowNew(clazz_operation_canceled_exception, "");
840 return NULL;
841 }
842 jobject result = env->NewObject(clazz_event, constructor_event);
843 env->SetIntField(result, field_event_eventCode, eventCode);
844 env->SetIntField(result, field_event_parameter1, static_cast<jint>(parameters[0]));
845 env->SetIntField(result, field_event_parameter2, static_cast<jint>(parameters[1]));
846 env->SetIntField(result, field_event_parameter3, static_cast<jint>(parameters[2]));
847 return result;
848 }
849
android_mtp_MtpDevice_discard_event_request(JNIEnv * env,jobject thiz,jint seq)850 static void android_mtp_MtpDevice_discard_event_request(JNIEnv *env, jobject thiz, jint seq)
851 {
852 MtpDevice* const device = get_device_from_object(env, thiz);
853 if (!device) {
854 return;
855 }
856 device->discardEventRequest(seq);
857 }
858
859 // Returns object size in 64-bit integer. If the MTP device does not support the property, it
860 // throws IOException.
android_mtp_MtpDevice_get_object_size_long(JNIEnv * env,jobject thiz,jint handle,jint format)861 static jlong android_mtp_MtpDevice_get_object_size_long(
862 JNIEnv *env, jobject thiz, jint handle, jint format) {
863 MtpDevice* const device = get_device_from_object(env, thiz);
864 if (!device) {
865 env->ThrowNew(clazz_io_exception, "Failed to obtain MtpDevice.");
866 return 0;
867 }
868
869 std::unique_ptr<MtpProperty> property(
870 device->getObjectPropDesc(MTP_PROPERTY_OBJECT_SIZE, format));
871 if (!property) {
872 env->ThrowNew(clazz_io_exception, "Failed to obtain property desc.");
873 return 0;
874 }
875
876 if (property->getDataType() != MTP_TYPE_UINT64) {
877 env->ThrowNew(clazz_io_exception, "Unexpected property data type.");
878 return 0;
879 }
880
881 if (!device->getObjectPropValue(handle, property.get())) {
882 env->ThrowNew(clazz_io_exception, "Failed to obtain property value.");
883 return 0;
884 }
885
886 const jlong object_size = static_cast<jlong>(property->getCurrentValue().u.u64);
887 if (object_size < 0) {
888 env->ThrowNew(clazz_io_exception, "Object size is too large to express as jlong.");
889 return 0;
890 }
891
892 return object_size;
893 }
894
895 // ----------------------------------------------------------------------------
896
897 static const JNINativeMethod gMethods[] = {
898 {"native_open", "(Ljava/lang/String;I)Z",
899 (void *)android_mtp_MtpDevice_open},
900 {"native_close", "()V", (void *)android_mtp_MtpDevice_close},
901 {"native_get_device_info", "()Landroid/mtp/MtpDeviceInfo;",
902 (void *)android_mtp_MtpDevice_get_device_info},
903 {"native_set_device_property_init_version", "(Ljava/lang/String;)I",
904 (void *)android_mtp_MtpDevice_set_device_property_init_version},
905 {"native_get_storage_ids", "()[I", (void *)android_mtp_MtpDevice_get_storage_ids},
906 {"native_get_storage_info", "(I)Landroid/mtp/MtpStorageInfo;",
907 (void *)android_mtp_MtpDevice_get_storage_info},
908 {"native_get_object_handles","(III)[I",
909 (void *)android_mtp_MtpDevice_get_object_handles},
910 {"native_get_object_info", "(I)Landroid/mtp/MtpObjectInfo;",
911 (void *)android_mtp_MtpDevice_get_object_info},
912 {"native_get_object", "(IJ)[B",(void *)android_mtp_MtpDevice_get_object},
913 {"native_get_partial_object", "(IJJ[B)J", (void *)android_mtp_MtpDevice_get_partial_object},
914 {"native_get_partial_object_64", "(IJJ[B)I",
915 (void *)android_mtp_MtpDevice_get_partial_object_64},
916 {"native_get_thumbnail", "(I)[B",(void *)android_mtp_MtpDevice_get_thumbnail},
917 {"native_delete_object", "(I)Z", (void *)android_mtp_MtpDevice_delete_object},
918 {"native_get_parent", "(I)I", (void *)android_mtp_MtpDevice_get_parent},
919 {"native_get_storage_id", "(I)I", (void *)android_mtp_MtpDevice_get_storage_id},
920 {"native_import_file", "(ILjava/lang/String;)Z",
921 (void *)android_mtp_MtpDevice_import_file},
922 {"native_import_file", "(II)Z",(void *)android_mtp_MtpDevice_import_file_to_fd},
923 {"native_send_object", "(IJI)Z",(void *)android_mtp_MtpDevice_send_object},
924 {"native_send_object_info", "(Landroid/mtp/MtpObjectInfo;)Landroid/mtp/MtpObjectInfo;",
925 (void *)android_mtp_MtpDevice_send_object_info},
926 {"native_submit_event_request", "()I", (void *)android_mtp_MtpDevice_submit_event_request},
927 {"native_reap_event_request", "(I)Landroid/mtp/MtpEvent;",
928 (void *)android_mtp_MtpDevice_reap_event_request},
929 {"native_discard_event_request", "(I)V", (void *)android_mtp_MtpDevice_discard_event_request},
930
931 {"native_get_object_size_long", "(II)J", (void *)android_mtp_MtpDevice_get_object_size_long},
932 };
933
register_android_mtp_MtpDevice(JNIEnv * env)934 int register_android_mtp_MtpDevice(JNIEnv *env)
935 {
936 ALOGD("register_android_mtp_MtpDevice\n");
937 return AndroidRuntime::registerNativeMethods(env,
938 "android/mtp/MtpDevice", gMethods, NELEM(gMethods));
939 }
940