diff options
| author | 2015-08-06 19:13:09 +0900 | |
|---|---|---|
| committer | 2015-08-14 13:09:52 +0900 | |
| commit | b04990599a1a05cc922b0fe9dd98128440efb56a (patch) | |
| tree | 3fa2cac3ca093e3249a9b6b7cf629f4a05a16f48 | |
| parent | 0ca8da52f76c1996a9ea6df866324855fd4d145f (diff) | |
Add API for uploading files to MTP devices.
Bug: 22545670
Change-Id: I038c54db06b7cf780bd027d76693e98c685d57a7
| -rw-r--r-- | api/current.txt | 27 | ||||
| -rw-r--r-- | api/system-current.txt | 27 | ||||
| -rw-r--r-- | media/java/android/mtp/MtpDevice.java | 29 | ||||
| -rw-r--r-- | media/java/android/mtp/MtpObjectInfo.java | 152 | ||||
| -rw-r--r-- | media/jni/android_mtp_MtpDevice.cpp | 173 |
5 files changed, 364 insertions, 44 deletions
diff --git a/api/current.txt b/api/current.txt index 47b8aeb0a59a..4ffd52c1a286 100644 --- a/api/current.txt +++ b/api/current.txt @@ -18091,6 +18091,8 @@ package android.mtp { method public boolean importFile(int, java.lang.String); method public boolean importFile(int, android.os.ParcelFileDescriptor); method public boolean open(android.hardware.usb.UsbDeviceConnection); + method public boolean sendObject(int, android.os.ParcelFileDescriptor); + method public android.mtp.MtpObjectInfo sendObjectInfo(android.mtp.MtpObjectInfo); } public class MtpDeviceInfo { @@ -18123,6 +18125,31 @@ package android.mtp { method public final int getThumbPixWidth(); } + public class MtpObjectInfo.Builder { + ctor public MtpObjectInfo.Builder(); + ctor public MtpObjectInfo.Builder(android.mtp.MtpObjectInfo); + method public android.mtp.MtpObjectInfo build(); + method public android.mtp.MtpObjectInfo.Builder setAssociationDesc(int); + method public android.mtp.MtpObjectInfo.Builder setAssociationType(int); + method public android.mtp.MtpObjectInfo.Builder setCompressedSize(int); + method public android.mtp.MtpObjectInfo.Builder setDateCreated(long); + method public android.mtp.MtpObjectInfo.Builder setDateModified(long); + method public android.mtp.MtpObjectInfo.Builder setFormat(int); + method public android.mtp.MtpObjectInfo.Builder setImagePixDepth(int); + method public android.mtp.MtpObjectInfo.Builder setImagePixHeight(int); + method public android.mtp.MtpObjectInfo.Builder setImagePixWidth(int); + method public android.mtp.MtpObjectInfo.Builder setKeywords(java.lang.String); + method public android.mtp.MtpObjectInfo.Builder setName(java.lang.String); + method public android.mtp.MtpObjectInfo.Builder setParent(int); + method public android.mtp.MtpObjectInfo.Builder setProtectionStatus(int); + method public android.mtp.MtpObjectInfo.Builder setSequenceNumber(int); + method public android.mtp.MtpObjectInfo.Builder setStorageId(int); + method public android.mtp.MtpObjectInfo.Builder setThumbCompressedSize(int); + method public android.mtp.MtpObjectInfo.Builder setThumbFormat(int); + method public android.mtp.MtpObjectInfo.Builder setThumbPixHeight(int); + method public android.mtp.MtpObjectInfo.Builder setThumbPixWidth(int); + } + public final class MtpStorageInfo { method public final java.lang.String getDescription(); method public final long getFreeSpace(); diff --git a/api/system-current.txt b/api/system-current.txt index 45a90e0bfde7..30086e666ffb 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -19603,6 +19603,8 @@ package android.mtp { method public boolean importFile(int, java.lang.String); method public boolean importFile(int, android.os.ParcelFileDescriptor); method public boolean open(android.hardware.usb.UsbDeviceConnection); + method public boolean sendObject(int, android.os.ParcelFileDescriptor); + method public android.mtp.MtpObjectInfo sendObjectInfo(android.mtp.MtpObjectInfo); } public class MtpDeviceInfo { @@ -19635,6 +19637,31 @@ package android.mtp { method public final int getThumbPixWidth(); } + public class MtpObjectInfo.Builder { + ctor public MtpObjectInfo.Builder(); + ctor public MtpObjectInfo.Builder(android.mtp.MtpObjectInfo); + method public android.mtp.MtpObjectInfo build(); + method public android.mtp.MtpObjectInfo.Builder setAssociationDesc(int); + method public android.mtp.MtpObjectInfo.Builder setAssociationType(int); + method public android.mtp.MtpObjectInfo.Builder setCompressedSize(int); + method public android.mtp.MtpObjectInfo.Builder setDateCreated(long); + method public android.mtp.MtpObjectInfo.Builder setDateModified(long); + method public android.mtp.MtpObjectInfo.Builder setFormat(int); + method public android.mtp.MtpObjectInfo.Builder setImagePixDepth(int); + method public android.mtp.MtpObjectInfo.Builder setImagePixHeight(int); + method public android.mtp.MtpObjectInfo.Builder setImagePixWidth(int); + method public android.mtp.MtpObjectInfo.Builder setKeywords(java.lang.String); + method public android.mtp.MtpObjectInfo.Builder setName(java.lang.String); + method public android.mtp.MtpObjectInfo.Builder setParent(int); + method public android.mtp.MtpObjectInfo.Builder setProtectionStatus(int); + method public android.mtp.MtpObjectInfo.Builder setSequenceNumber(int); + method public android.mtp.MtpObjectInfo.Builder setStorageId(int); + method public android.mtp.MtpObjectInfo.Builder setThumbCompressedSize(int); + method public android.mtp.MtpObjectInfo.Builder setThumbFormat(int); + method public android.mtp.MtpObjectInfo.Builder setThumbPixHeight(int); + method public android.mtp.MtpObjectInfo.Builder setThumbPixWidth(int); + } + public final class MtpStorageInfo { method public final java.lang.String getDescription(); method public final long getFreeSpace(); diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java index a9611c52f75e..fbe047dea1d7 100644 --- a/media/java/android/mtp/MtpDevice.java +++ b/media/java/android/mtp/MtpDevice.java @@ -250,6 +250,33 @@ public final class MtpDevice { return native_import_file(objectHandle, descriptor.getFd()); } + /** + * Copies the data for an object from a file descriptor. + * This call may block for an arbitrary amount of time depending on the size + * of the data and speed of the devices. The file descriptor is not closed + * on completion, and must be done by the caller. + * + * @param objectHandle handle of the target file + * @param descriptor file descriptor to read the data from. + * @return true if the file transfer succeeds + */ + public boolean sendObject(int objectHandle, ParcelFileDescriptor descriptor) { + return native_send_object(objectHandle, descriptor.getFd()); + } + + /** + * Uploads an object metadata for a new entry. The {@link MtpObjectInfo} can be + * created with the {@link MtpObjectInfo.Builder} class. + * + * The returned {@link MtpObjectInfo} has the new object handle field filled in. + * + * @param info metadata of the entry + * @return object info of the created entry + */ + public MtpObjectInfo sendObjectInfo(MtpObjectInfo info) { + return native_send_object_info(info); + } + // used by the JNI code private long mNativeContext; @@ -267,4 +294,6 @@ public final class MtpDevice { private native long native_get_storage_id(int objectHandle); private native boolean native_import_file(int objectHandle, String destPath); private native boolean native_import_file(int objectHandle, int fd); + private native boolean native_send_object(int objectHandle, int fd); + private native MtpObjectInfo native_send_object_info(MtpObjectInfo info); } diff --git a/media/java/android/mtp/MtpObjectInfo.java b/media/java/android/mtp/MtpObjectInfo.java index 5bbfe9a6888f..d2824b556315 100644 --- a/media/java/android/mtp/MtpObjectInfo.java +++ b/media/java/android/mtp/MtpObjectInfo.java @@ -43,7 +43,7 @@ public final class MtpObjectInfo { private long mDateModified; private String mKeywords; - // only instantiated via JNI + // only instantiated via JNI or via a builder private MtpObjectInfo() { } @@ -252,4 +252,154 @@ public final class MtpObjectInfo { public final String getKeywords() { return mKeywords; } + + /** + * Builds a new object info instance. + */ + public class Builder { + private MtpObjectInfo mObjectInfo; + + public Builder() { + mObjectInfo = new MtpObjectInfo(); + mObjectInfo.mHandle = -1; + } + + /** + * Creates a builder on a copy of an existing object info. + * All fields, except the object handle will be copied. + * + * @param objectInfo object info of an existing entry + */ + public Builder(MtpObjectInfo objectInfo) { + mObjectInfo = new MtpObjectInfo(); + mObjectInfo.mHandle = -1; + mObjectInfo.mAssociationDesc = mObjectInfo.mAssociationDesc; + mObjectInfo.mAssociationType = mObjectInfo.mAssociationType; + mObjectInfo.mCompressedSize = mObjectInfo.mCompressedSize; + mObjectInfo.mDateCreated = mObjectInfo.mDateCreated; + mObjectInfo.mDateModified = mObjectInfo.mDateModified; + mObjectInfo.mFormat = mObjectInfo.mFormat; + mObjectInfo.mImagePixDepth = mObjectInfo.mImagePixDepth; + mObjectInfo.mImagePixHeight = mObjectInfo.mImagePixHeight; + mObjectInfo.mImagePixWidth = mObjectInfo.mImagePixWidth; + mObjectInfo.mKeywords = mObjectInfo.mKeywords; + mObjectInfo.mName = mObjectInfo.mName; + mObjectInfo.mParent = mObjectInfo.mParent; + mObjectInfo.mProtectionStatus = mObjectInfo.mProtectionStatus; + mObjectInfo.mSequenceNumber = mObjectInfo.mSequenceNumber; + mObjectInfo.mStorageId = mObjectInfo.mStorageId; + mObjectInfo.mThumbCompressedSize = mObjectInfo.mThumbCompressedSize; + mObjectInfo.mThumbFormat = mObjectInfo.mThumbFormat; + mObjectInfo.mThumbPixHeight = mObjectInfo.mThumbPixHeight; + mObjectInfo.mThumbPixWidth = mObjectInfo.mThumbPixWidth; + } + + public Builder setAssociationDesc(int value) { + mObjectInfo.mAssociationDesc = value; + return this; + } + + public Builder setAssociationType(int value) { + mObjectInfo.mAssociationType = value; + return this; + } + + public Builder setCompressedSize(int value) { + mObjectInfo.mCompressedSize = value; + return this; + } + + public Builder setDateCreated(long value) { + mObjectInfo.mDateCreated = value; + return this; + } + + public Builder setDateModified(long value) { + mObjectInfo.mDateModified = value; + return this; + } + + public Builder setFormat(int value) { + mObjectInfo.mFormat = value; + return this; + } + + public Builder setImagePixDepth(int value) { + mObjectInfo.mImagePixDepth = value; + return this; + } + + public Builder setImagePixHeight(int value) { + mObjectInfo.mImagePixHeight = value; + return this; + } + + public Builder setImagePixWidth(int value) { + mObjectInfo.mImagePixWidth = value; + return this; + } + + public Builder setKeywords(String value) { + mObjectInfo.mKeywords = value; + return this; + } + + public Builder setName(String value) { + mObjectInfo.mName = value; + return this; + } + + public Builder setParent(int value) { + mObjectInfo.mParent = value; + return this; + } + + public Builder setProtectionStatus(int value) { + mObjectInfo.mProtectionStatus = value; + return this; + } + + public Builder setSequenceNumber(int value) { + mObjectInfo.mSequenceNumber = value; + return this; + } + + public Builder setStorageId(int value) { + mObjectInfo.mStorageId = value; + return this; + } + + public Builder setThumbCompressedSize(int value) { + mObjectInfo.mThumbCompressedSize = value; + return this; + } + + public Builder setThumbFormat(int value) { + mObjectInfo.mThumbFormat = value; + return this; + } + + public Builder setThumbPixHeight(int value) { + mObjectInfo.mThumbPixHeight = value; + return this; + } + + public Builder setThumbPixWidth(int value) { + mObjectInfo.mThumbPixWidth = value; + return this; + } + + /** + * Builds the object info instance. Once called, methods of the builder + * must not be called anymore. + * + * @return the object info of the newly created file, or NULL in case + * of an error. + */ + public MtpObjectInfo build() { + MtpObjectInfo result = mObjectInfo; + mObjectInfo = null; + return result; + } + } } diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp index 6652a54506e0..ad804f32fd9f 100644 --- a/media/jni/android_mtp_MtpDevice.cpp +++ b/media/jni/android_mtp_MtpDevice.cpp @@ -91,6 +91,55 @@ MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice) return (MtpDevice*)env->GetLongField(javaDevice, field_context); } +void fill_jobject_from_object_info(JNIEnv* env, jobject object, MtpObjectInfo* objectInfo) { + if (objectInfo->mHandle) + env->SetIntField(object, field_objectInfo_handle, objectInfo->mHandle); + if (objectInfo->mStorageID) + env->SetIntField(object, field_objectInfo_storageId, objectInfo->mStorageID); + if (objectInfo->mFormat) + env->SetIntField(object, field_objectInfo_format, objectInfo->mFormat); + if (objectInfo->mProtectionStatus) + env->SetIntField(object, field_objectInfo_protectionStatus, objectInfo->mProtectionStatus); + if (objectInfo->mCompressedSize) + env->SetIntField(object, field_objectInfo_compressedSize, objectInfo->mCompressedSize); + if (objectInfo->mThumbFormat) + env->SetIntField(object, field_objectInfo_thumbFormat, objectInfo->mThumbFormat); + if (objectInfo->mThumbCompressedSize) { + env->SetIntField(object, field_objectInfo_thumbCompressedSize, + objectInfo->mThumbCompressedSize); + } + if (objectInfo->mThumbPixWidth) + env->SetIntField(object, field_objectInfo_thumbPixWidth, objectInfo->mThumbPixWidth); + if (objectInfo->mThumbPixHeight) + env->SetIntField(object, field_objectInfo_thumbPixHeight, objectInfo->mThumbPixHeight); + if (objectInfo->mImagePixWidth) + env->SetIntField(object, field_objectInfo_imagePixWidth, objectInfo->mImagePixWidth); + if (objectInfo->mImagePixHeight) + env->SetIntField(object, field_objectInfo_imagePixHeight, objectInfo->mImagePixHeight); + if (objectInfo->mImagePixDepth) + env->SetIntField(object, field_objectInfo_imagePixDepth, objectInfo->mImagePixDepth); + if (objectInfo->mParent) + env->SetIntField(object, field_objectInfo_parent, objectInfo->mParent); + if (objectInfo->mAssociationType) + env->SetIntField(object, field_objectInfo_associationType, objectInfo->mAssociationType); + if (objectInfo->mAssociationDesc) + env->SetIntField(object, field_objectInfo_associationDesc, objectInfo->mAssociationDesc); + if (objectInfo->mSequenceNumber) + env->SetIntField(object, field_objectInfo_sequenceNumber, objectInfo->mSequenceNumber); + if (objectInfo->mName) + env->SetObjectField(object, field_objectInfo_name, env->NewStringUTF(objectInfo->mName)); + if (objectInfo->mDateCreated) + env->SetLongField(object, field_objectInfo_dateCreated, objectInfo->mDateCreated * 1000LL); + if (objectInfo->mDateModified) { + env->SetLongField(object, field_objectInfo_dateModified, + objectInfo->mDateModified * 1000LL); + } + if (objectInfo->mKeywords) { + env->SetObjectField(object, field_objectInfo_keywords, + env->NewStringUTF(objectInfo->mKeywords)); + } +} + // ---------------------------------------------------------------------------- static jboolean @@ -246,48 +295,7 @@ android_mtp_MtpDevice_get_object_info(JNIEnv *env, jobject thiz, jint objectID) return NULL; } - if (objectInfo->mHandle) - env->SetIntField(info, field_objectInfo_handle, objectInfo->mHandle); - if (objectInfo->mStorageID) - env->SetIntField(info, field_objectInfo_storageId, objectInfo->mStorageID); - if (objectInfo->mFormat) - env->SetIntField(info, field_objectInfo_format, objectInfo->mFormat); - if (objectInfo->mProtectionStatus) - env->SetIntField(info, field_objectInfo_protectionStatus, objectInfo->mProtectionStatus); - if (objectInfo->mCompressedSize) - env->SetIntField(info, field_objectInfo_compressedSize, objectInfo->mCompressedSize); - if (objectInfo->mThumbFormat) - env->SetIntField(info, field_objectInfo_thumbFormat, objectInfo->mThumbFormat); - if (objectInfo->mThumbCompressedSize) - env->SetIntField(info, field_objectInfo_thumbCompressedSize, objectInfo->mThumbCompressedSize); - if (objectInfo->mThumbPixWidth) - env->SetIntField(info, field_objectInfo_thumbPixWidth, objectInfo->mThumbPixWidth); - if (objectInfo->mThumbPixHeight) - env->SetIntField(info, field_objectInfo_thumbPixHeight, objectInfo->mThumbPixHeight); - if (objectInfo->mImagePixWidth) - env->SetIntField(info, field_objectInfo_imagePixWidth, objectInfo->mImagePixWidth); - if (objectInfo->mImagePixHeight) - env->SetIntField(info, field_objectInfo_imagePixHeight, objectInfo->mImagePixHeight); - if (objectInfo->mImagePixDepth) - env->SetIntField(info, field_objectInfo_imagePixDepth, objectInfo->mImagePixDepth); - if (objectInfo->mParent) - env->SetIntField(info, field_objectInfo_parent, objectInfo->mParent); - if (objectInfo->mAssociationType) - env->SetIntField(info, field_objectInfo_associationType, objectInfo->mAssociationType); - if (objectInfo->mAssociationDesc) - env->SetIntField(info, field_objectInfo_associationDesc, objectInfo->mAssociationDesc); - if (objectInfo->mSequenceNumber) - env->SetIntField(info, field_objectInfo_sequenceNumber, objectInfo->mSequenceNumber); - if (objectInfo->mName) - env->SetObjectField(info, field_objectInfo_name, env->NewStringUTF(objectInfo->mName)); - if (objectInfo->mDateCreated) - env->SetLongField(info, field_objectInfo_dateCreated, objectInfo->mDateCreated * 1000LL); - if (objectInfo->mDateModified) - env->SetLongField(info, field_objectInfo_dateModified, objectInfo->mDateModified * 1000LL); - if (objectInfo->mKeywords) - env->SetObjectField(info, field_objectInfo_keywords, - env->NewStringUTF(objectInfo->mKeywords)); - + fill_jobject_from_object_info(env, info, objectInfo); delete objectInfo; return info; } @@ -403,6 +411,82 @@ android_mtp_MtpDevice_import_file_to_fd(JNIEnv *env, jobject thiz, jint object_i return JNI_FALSE; } +static jboolean +android_mtp_MtpDevice_send_object(JNIEnv *env, jobject thiz, jint object_id, jint fd) +{ + MtpDevice* device = get_device_from_object(env, thiz); + if (!device) + return JNI_FALSE; + MtpObjectInfo* object_info = device->getObjectInfo(object_id); + if (!object_info) + return JNI_FALSE; + + bool result = device->sendObject(object_info, fd); + delete object_info; + return result; +} + +static jobject +android_mtp_MtpDevice_send_object_info(JNIEnv *env, jobject thiz, jobject info) +{ + MtpDevice* device = get_device_from_object(env, thiz); + if (!device) + return JNI_FALSE; + + // Updating existing objects is not supported. + if (env->GetIntField(info, field_objectInfo_handle) != -1) + return JNI_FALSE; + + MtpObjectInfo* object_info = new MtpObjectInfo(-1); + object_info->mStorageID = env->GetIntField(info, field_objectInfo_storageId); + object_info->mFormat = env->GetIntField(info, field_objectInfo_format); + object_info->mProtectionStatus = env->GetIntField(info, field_objectInfo_protectionStatus); + object_info->mCompressedSize = env->GetIntField(info, field_objectInfo_compressedSize); + object_info->mThumbFormat = env->GetIntField(info, field_objectInfo_thumbFormat); + object_info->mThumbCompressedSize = + env->GetIntField(info, field_objectInfo_thumbCompressedSize); + object_info->mThumbPixWidth = env->GetIntField(info, field_objectInfo_thumbPixWidth); + object_info->mThumbPixHeight = env->GetIntField(info, field_objectInfo_thumbPixHeight); + object_info->mImagePixWidth = env->GetIntField(info, field_objectInfo_imagePixWidth); + object_info->mImagePixHeight = env->GetIntField(info, field_objectInfo_imagePixHeight); + object_info->mImagePixDepth = env->GetIntField(info, field_objectInfo_imagePixDepth); + object_info->mParent = env->GetIntField(info, field_objectInfo_parent); + object_info->mAssociationType = env->GetIntField(info, field_objectInfo_associationType); + object_info->mAssociationDesc = env->GetIntField(info, field_objectInfo_associationDesc); + object_info->mSequenceNumber = env->GetIntField(info, field_objectInfo_sequenceNumber); + + jstring name_jstring = (jstring) env->GetObjectField(info, field_objectInfo_name); + const char* name_string = env->GetStringUTFChars(name_jstring, NULL); + object_info->mName = strdup(name_string); + env->ReleaseStringUTFChars(name_jstring, name_string); + + object_info->mDateCreated = env->GetLongField(info, field_objectInfo_dateCreated) / 1000LL; + object_info->mDateModified = env->GetLongField(info, field_objectInfo_dateModified) / 1000LL; + + jstring keywords_jstring = (jstring) env->GetObjectField(info, field_objectInfo_keywords); + const char* keywords_string = env->GetStringUTFChars(keywords_jstring, NULL); + object_info->mKeywords = strdup(keywords_string); + env->ReleaseStringUTFChars(keywords_jstring, keywords_string); + + int object_handle = device->sendObjectInfo(object_info); + if (object_handle == -1) { + delete object_info; + return NULL; + } + + object_info->mHandle = object_handle; + jobject result = env->NewObject(clazz_objectInfo, constructor_objectInfo); + if (result == NULL) { + ALOGE("Could not create a MtpObjectInfo object"); + delete object_info; + return NULL; + } + + fill_jobject_from_object_info(env, result, object_info); + delete object_info; + return result; +} + // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { @@ -425,7 +509,10 @@ static JNINativeMethod gMethods[] = { {"native_get_storage_id", "(I)J", (void *)android_mtp_MtpDevice_get_storage_id}, {"native_import_file", "(ILjava/lang/String;)Z", (void *)android_mtp_MtpDevice_import_file}, - {"native_import_file", "(II)Z", (void *)android_mtp_MtpDevice_import_file_to_fd} + {"native_import_file", "(II)Z",(void *)android_mtp_MtpDevice_import_file_to_fd}, + {"native_send_object", "(II)Z",(void *)android_mtp_MtpDevice_send_object}, + {"native_send_object_info", "(Landroid/mtp/MtpObjectInfo;)Landroid/mtp/MtpObjectInfo;", + (void *)android_mtp_MtpDevice_send_object_info} }; int register_android_mtp_MtpDevice(JNIEnv *env) |