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;