diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 4bf23fc1840ec..293d31c0faf03 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -237,6 +237,14 @@ public final class MediaStore { *

Type: TEXT

*/ public static final String MIME_TYPE = "mime_type"; + + /** + * The MTP object handle of a newly transfered file. + * Used internally by the MediaScanner + *

Type: INTEGER

+ * @hide + */ + public static final String MTP_OBJECT_HANDLE = "mtp_object_handle"; } diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 0decb1d0b3175..a346ae46201da 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -107,6 +107,9 @@ public class MediaFile { // maps mime type to MTP format code private static HashMap sMimeTypeToFormatMap = new HashMap(); + // maps MTP format code to mime type + private static HashMap sFormatToMimeTypeMap + = new HashMap(); static void addFileType(String extension, int fileType, String mimeType) { sFileTypeMap.put(extension, new MediaFileType(fileType, mimeType)); @@ -117,6 +120,7 @@ public class MediaFile { addFileType(extension, fileType, mimeType); sFileTypeToFormatMap.put(extension, Integer.valueOf(mtpFormatCode)); sMimeTypeToFormatMap.put(mimeType, Integer.valueOf(mtpFormatCode)); + sFormatToMimeTypeMap.put(mtpFormatCode, mimeType); } private static boolean isWMAEnabled() { @@ -253,4 +257,8 @@ public class MediaFile { } return Mtp.Object.FORMAT_UNDEFINED; } + + public static String getMimeTypeForFormatCode(int formatCode) { + return sFormatToMimeTypeMap.get(formatCode); + } } diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 3333268beb9f5..7cbe409c942e7 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -305,6 +305,7 @@ public class MediaScanner private Uri mGenresUri; private Uri mPlaylistsUri; private boolean mProcessPlaylists, mProcessGenres; + private int mMtpObjectHandle; // used when scanning the image database so we know whether we have to prune // old thumbnail files @@ -625,6 +626,9 @@ public class MediaScanner map.put(MediaStore.MediaColumns.DATE_MODIFIED, mLastModified); map.put(MediaStore.MediaColumns.SIZE, mFileSize); map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType); + if (mMtpObjectHandle != 0) { + map.put(MediaStore.MediaColumns.MTP_OBJECT_HANDLE, mMtpObjectHandle); + } if (MediaFile.isVideoFileType(mFileType)) { map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0 ? mArtist : MediaStore.UNKNOWN_STRING)); @@ -1227,6 +1231,14 @@ public class MediaScanner } } + public Uri scanMtpFile(String path, String volumeName, int objectHandle, int format) { + String mimeType = MediaFile.getMimeTypeForFormatCode(format); + mMtpObjectHandle = objectHandle; + Uri result = scanSingleFile(path, volumeName, mimeType); + mMtpObjectHandle = 0; + return result; + } + // returns the number of matching file/directory names, starting from the right private int matchPaths(String path1, String path2) { int result = 0; diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java index e37ea930e6c24..758999ea0e0b0 100644 --- a/media/java/android/media/MtpDatabase.java +++ b/media/java/android/media/MtpDatabase.java @@ -17,6 +17,7 @@ package android.media; import android.content.Context; +import android.content.ContentValues; import android.content.IContentProvider; import android.database.Cursor; import android.net.Uri; @@ -57,6 +58,8 @@ public class MtpDatabase { private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND " + MtpObjects.ObjectColumns.FORMAT + "=?"; + private final MediaScanner mMediaScanner; + static { System.loadLibrary("media_jni"); } @@ -67,6 +70,7 @@ public class MtpDatabase { mMediaProvider = context.getContentResolver().acquireProvider("media"); mVolumeName = volumeName; mObjectsUri = MtpObjects.getContentUri(volumeName); + mMediaScanner = new MediaScanner(context); } @Override @@ -74,10 +78,35 @@ public class MtpDatabase { native_finalize(); } - private int addFile(String path, int format, int parent, + private int beginSendObject(String path, int format, int parent, int storage, long size, long modified) { - Log.d(TAG, "addFile " + path); - return 0; + ContentValues values = new ContentValues(); + values.put(MtpObjects.ObjectColumns.DATA, path); + values.put(MtpObjects.ObjectColumns.FORMAT, format); + values.put(MtpObjects.ObjectColumns.PARENT, parent); + // storage is ignored for now + values.put(MtpObjects.ObjectColumns.SIZE, size); + values.put(MtpObjects.ObjectColumns.DATE_MODIFIED, modified); + + try { + Uri uri = mMediaProvider.insert(mObjectsUri, values); + if (uri != null) { + return Integer.parseInt(uri.getPathSegments().get(2)); + } else { + return -1; + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in beginSendObject", e); + return -1; + } + } + + private void endSendObject(String path, int handle, int format, boolean succeeded) { + if (succeeded) { + Uri uri = mMediaScanner.scanMtpFile(path, mVolumeName, handle, format); + } else { + deleteFile(handle); + } } private int[] getObjectList(int storageID, int format, int parent) { diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp index 6bdd8f028e343..53e41e82c43c6 100644 --- a/media/jni/android_media_MtpDatabase.cpp +++ b/media/jni/android_media_MtpDatabase.cpp @@ -36,7 +36,8 @@ using namespace android; // ---------------------------------------------------------------------------- -static jmethodID method_addFile; +static jmethodID method_beginSendObject; +static jmethodID method_endSendObject; static jmethodID method_getObjectList; static jmethodID method_getObjectProperty; static jmethodID method_getObjectInfo; @@ -62,13 +63,18 @@ public: virtual ~MyMtpDatabase(); void cleanup(JNIEnv *env); - virtual MtpObjectHandle addFile(const char* path, + virtual MtpObjectHandle beginSendObject(const char* path, MtpObjectFormat format, MtpObjectHandle parent, MtpStorageID storage, uint64_t size, time_t modified); + virtual void endSendObject(const char* path, + MtpObjectHandle handle, + MtpObjectFormat format, + bool succeeded); + virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent); @@ -135,17 +141,24 @@ void MyMtpDatabase::cleanup(JNIEnv *env) { MyMtpDatabase::~MyMtpDatabase() { } -MtpObjectHandle MyMtpDatabase::addFile(const char* path, +MtpObjectHandle MyMtpDatabase::beginSendObject(const char* path, MtpObjectFormat format, MtpObjectHandle parent, MtpStorageID storage, uint64_t size, time_t modified) { JNIEnv* env = AndroidRuntime::getJNIEnv(); - return env->CallIntMethod(mDatabase, method_addFile, env->NewStringUTF(path), + return env->CallIntMethod(mDatabase, method_beginSendObject, env->NewStringUTF(path), (jint)format, (jint)parent, (jint)storage, (jlong)size, (jlong)modified); } +void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle, + MtpObjectFormat format, bool succeeded) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mDatabase, method_endSendObject, env->NewStringUTF(path), + (jint)handle, (jint)format, (jboolean)succeeded); +} + MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent) { @@ -397,9 +410,14 @@ int register_android_media_MtpDatabase(JNIEnv *env) LOGE("Can't find android/media/MtpDatabase"); return -1; } - method_addFile = env->GetMethodID(clazz, "addFile", "(Ljava/lang/String;IIIJJ)I"); - if (method_addFile == NULL) { - LOGE("Can't find addFile"); + method_beginSendObject = env->GetMethodID(clazz, "beginSendObject", "(Ljava/lang/String;IIIJJ)I"); + if (method_beginSendObject == NULL) { + LOGE("Can't find beginSendObject"); + return -1; + } + method_endSendObject = env->GetMethodID(clazz, "endSendObject", "(Ljava/lang/String;IIZ)V"); + if (method_endSendObject == NULL) { + LOGE("Can't find endSendObject"); return -1; } method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I"); diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h index 1566a111e62a3..7feb3dc245fd5 100644 --- a/media/mtp/MtpDatabase.h +++ b/media/mtp/MtpDatabase.h @@ -27,16 +27,25 @@ class MtpDatabase { public: virtual ~MtpDatabase() {} - virtual MtpObjectHandle addFile(const char* path, + // called from SendObjectInfo to reserve a database entry for the incoming file + virtual MtpObjectHandle beginSendObject(const char* path, MtpObjectFormat format, MtpObjectHandle parent, MtpStorageID storage, uint64_t size, time_t modified) = 0; + // called to report success or failure of the SendObject file transfer + // success should signal a notification of the new object's creation, + // failure should remove the database entry created in beginSendObject + virtual void endSendObject(const char* path, + MtpObjectHandle handle, + MtpObjectFormat format, + bool succeeded) = 0; + virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID, - MtpObjectFormat format, - MtpObjectHandle parent) = 0; + MtpObjectFormat format, + MtpObjectHandle parent) = 0; virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle, MtpObjectProperty property, diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index 5a16a0385d8bc..e8f09faebbb6e 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -123,6 +123,7 @@ MtpServer::MtpServer(int fd, MtpDatabase* database, mSessionID(0), mSessionOpen(false), mSendObjectHandle(kInvalidObjectHandle), + mSendObjectFormat(0), mSendObjectFileSize(0) { initObjectProperties(); @@ -519,8 +520,8 @@ MtpResponseCode MtpServer::doSendObjectInfo() { path += (const char *)name; mDatabase->beginTransaction(); - MtpObjectHandle handle = mDatabase->addFile((const char*)path, format, parent, storageID, - mSendObjectFileSize, modifiedTime); + MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, + format, parent, storageID, mSendObjectFileSize, modifiedTime); if (handle == kInvalidObjectHandle) { mDatabase->rollbackTransaction(); return MTP_RESPONSE_GENERAL_ERROR; @@ -538,6 +539,7 @@ MtpResponseCode MtpServer::doSendObjectInfo() { mSendObjectFilePath = path; // save the handle for the SendObject call, which should follow mSendObjectHandle = handle; + mSendObjectFormat = format; } mResponse.setParameter(1, storageID); @@ -548,13 +550,18 @@ MtpResponseCode MtpServer::doSendObjectInfo() { } MtpResponseCode MtpServer::doSendObject() { + MtpResponseCode result = MTP_RESPONSE_OK; + mode_t mask; + int ret; + if (mSendObjectHandle == kInvalidObjectHandle) { LOGE("Expected SendObjectInfo before SendObject"); - return MTP_RESPONSE_NO_VALID_OBJECT_INFO; + result = MTP_RESPONSE_NO_VALID_OBJECT_INFO; + goto done; } // read the header - int ret = mData.readDataHeader(mFD); + ret = mData.readDataHeader(mFD); // FIXME - check for errors here. // reset so we don't attempt to send this back @@ -563,11 +570,12 @@ MtpResponseCode MtpServer::doSendObject() { mtp_file_range mfr; mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC); if (mfr.fd < 0) { - return MTP_RESPONSE_GENERAL_ERROR; + result = MTP_RESPONSE_GENERAL_ERROR; + goto done; } fchown(mfr.fd, getuid(), mFileGroup); // set permissions - mode_t mask = umask(0); + mask = umask(0); fchmod(mfr.fd, mFilePermission); umask(mask); @@ -578,18 +586,22 @@ MtpResponseCode MtpServer::doSendObject() { ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); close(mfr.fd); - // FIXME - we need to delete mSendObjectHandle from the database if this fails. LOGV("MTP_RECEIVE_FILE returned %d", ret); - mSendObjectHandle = kInvalidObjectHandle; if (ret < 0) { unlink(mSendObjectFilePath); if (errno == ECANCELED) - return MTP_RESPONSE_TRANSACTION_CANCELLED; + result = MTP_RESPONSE_TRANSACTION_CANCELLED; else - return MTP_RESPONSE_GENERAL_ERROR; + result = MTP_RESPONSE_GENERAL_ERROR; } - return MTP_RESPONSE_OK; + +done: + mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat, + result == MTP_RESPONSE_OK); + mSendObjectHandle = kInvalidObjectHandle; + mSendObjectFormat = 0; + return result; } MtpResponseCode MtpServer::doDeleteObject() { diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h index afba8460b4779..37b1cbe82a0a0 100644 --- a/media/mtp/MtpServer.h +++ b/media/mtp/MtpServer.h @@ -60,6 +60,7 @@ private: // handle for new object, set by SendObjectInfo and used by SendObject MtpObjectHandle mSendObjectHandle; + MtpObjectFormat mSendObjectFormat; MtpString mSendObjectFilePath; size_t mSendObjectFileSize;