• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &parameters);
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