diff options
author | Mike Lockwood <lockwood@android.com> | 2010-07-12 08:49:01 -0400 |
---|---|---|
committer | Mike Lockwood <lockwood@android.com> | 2010-07-12 14:01:22 -0400 |
commit | d815f79766984fce499e147ecbacc01914683f74 (patch) | |
tree | e798bbbe4205352314d9252f73833292115876c8 /media | |
parent | 19046b0185f2331c86075419f2608cf235be1253 (diff) | |
download | frameworks_base-d815f79766984fce499e147ecbacc01914683f74.zip frameworks_base-d815f79766984fce499e147ecbacc01914683f74.tar.gz frameworks_base-d815f79766984fce499e147ecbacc01914683f74.tar.bz2 |
Integrate host to device file transfer with the media provider.
MTP file transfers happen in two stages. The SendObjectInfo command sends
some information about the file and reserves an ObjectHandle for the new file.
The file transfer is then performed using the SendObject command.
To support this in the media provider, MtpDatabase.beginSendObject receives
the information from SendObjectInfo and creates an row for it in the MTP objects
table for the new file. After the file transfer has completed, then
MtpDatabase.endSendObject is called. In endSendObject, we run the media scanner
on the new file, which will add a row to the images, audio, video
or audio playlist table.
To avoid the media scanner creating a second row for the file in the MTP objects
table, we pass the ObjectHandle created in beginSendObject to the media scanner,
which then passes it to the media provider via the content values when it
performs its insert.
Change-Id: I1ebcc63d6bd4404b0d3a93c703a9d3c097381d3a
Signed-off-by: Mike Lockwood <lockwood@android.com>
Diffstat (limited to 'media')
-rw-r--r-- | media/java/android/media/MediaFile.java | 8 | ||||
-rw-r--r-- | media/java/android/media/MediaScanner.java | 12 | ||||
-rw-r--r-- | media/java/android/media/MtpDatabase.java | 35 | ||||
-rw-r--r-- | media/jni/android_media_MtpDatabase.cpp | 32 | ||||
-rw-r--r-- | media/mtp/MtpDatabase.h | 15 | ||||
-rw-r--r-- | media/mtp/MtpServer.cpp | 34 | ||||
-rw-r--r-- | media/mtp/MtpServer.h | 1 |
7 files changed, 113 insertions, 24 deletions
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 0decb1d..a346ae4 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<String, Integer> sMimeTypeToFormatMap = new HashMap<String, Integer>(); + // maps MTP format code to mime type + private static HashMap<Integer, String> sFormatToMimeTypeMap + = new HashMap<Integer, String>(); 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 3333268..7cbe409 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 e37ea93..758999e 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 6bdd8f0..53e41e8 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 1566a11..7feb3dc 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 5a16a03..e8f09fa 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 afba846..37b1cbe 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; |