Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c3cc7011 authored by Mike Lockwood's avatar Mike Lockwood Committed by Android (Google) Code Review
Browse files

Merge changes I085cb3cf,Ied1cddc6

* changes:
  MTP: Add support for syncing MTP playlists
  MTP: Fix some thread safety issues in MTP server start/stop sequence.
parents a7dd5eab 9a2046fb
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -273,6 +273,13 @@ public final class MediaStore {
                    + "/object/" + objectId);
                    + "/object/" + objectId);
        }
        }


        // used for MTP GetObjectReferences and SetObjectReferences
        public static final Uri getReferencesUri(String volumeName,
                long objectId) {
            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
                    + "/object/" + objectId + "/references");
        }

        /**
        /**
         * Fields for master table for all media files.
         * Fields for master table for all media files.
         * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
         * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
+77 −1
Original line number Original line Diff line number Diff line
@@ -22,7 +22,10 @@ import android.content.IContentProvider;
import android.database.Cursor;
import android.database.Cursor;
import android.net.Uri;
import android.net.Uri;
import android.os.RemoteException;
import android.os.RemoteException;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.MediaColumns;
import android.provider.MediaStore.MtpObjects;
import android.provider.MediaStore.MtpObjects;
import android.provider.Mtp;
import android.util.Log;
import android.util.Log;


/**
/**
@@ -120,7 +123,33 @@ public class MtpDatabase {


    private void endSendObject(String path, int handle, int format, boolean succeeded) {
    private void endSendObject(String path, int handle, int format, boolean succeeded) {
        if (succeeded) {
        if (succeeded) {
            // handle abstract playlists separately
            // they do not exist in the file system so don't use the media scanner here
            if (format == Mtp.Object.FORMAT_ABSTRACT_AV_PLAYLIST) {
                // Strip Windows Media Player file extension
                if (path.endsWith(".pla")) {
                    path = path.substring(0, path.length() - 4);
                }

                // extract name from path
                String name = path;
                int lastSlash = name.lastIndexOf('/');
                if (lastSlash >= 0) {
                    name = name.substring(lastSlash + 1);
                }

                ContentValues values = new ContentValues(1);
                values.put(Audio.Playlists.DATA, path);
                values.put(Audio.Playlists.NAME, name);
                values.put(MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, handle);
                try {
                    Uri uri = mMediaProvider.insert(Audio.Playlists.EXTERNAL_CONTENT_URI, values);
                } catch (RemoteException e) {
                    Log.e(TAG, "RemoteException in endSendObject", e);
                }
            } else {
                Uri uri = mMediaScanner.scanMtpFile(path, mVolumeName, handle, format);
                Uri uri = mMediaScanner.scanMtpFile(path, mVolumeName, handle, format);
            }
        } else {
        } else {
            deleteFile(handle);
            deleteFile(handle);
        }
        }
@@ -338,6 +367,53 @@ public class MtpDatabase {
        }
        }
    }
    }


    private int[] getObjectReferences(int handle) {
        Log.d(TAG, "getObjectReferences for: " + handle);
        Uri uri = MtpObjects.getReferencesUri(mVolumeName, handle);
        Cursor c = null;
        try {
            c = mMediaProvider.query(uri, ID_PROJECTION, null, null, null);
            if (c == null) {
                return null;
            }
            int count = c.getCount();
            if (count > 0) {
                int[] result = new int[count];
                for (int i = 0; i < count; i++) {
                    c.moveToNext();
                    result[i] = c.getInt(0);
                }
                return result;
            }
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in getObjectList", e);
        } finally {
            if (c != null) {
                c.close();
            }
        }
        return null;
    }

    private int setObjectReferences(int handle, int[] references) {
        Uri uri = MtpObjects.getReferencesUri(mVolumeName, handle);
        int count = references.length;
        ContentValues[] valuesList = new ContentValues[count];
        for (int i = 0; i < count; i++) {
            ContentValues values = new ContentValues();
            values.put(MtpObjects.ObjectColumns._ID, references[i]);
            valuesList[i] = values;
        }
        try {
            if (count == mMediaProvider.bulkInsert(uri, valuesList)) {
                return MTP_RESPONSE_OK;
            }
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in setObjectReferences", e);
        }
        return MTP_RESPONSE_GENERAL_ERROR;
    }

    // used by the JNI code
    // used by the JNI code
    private int mNativeContext;
    private int mNativeContext;


+48 −0
Original line number Original line Diff line number Diff line
@@ -44,6 +44,8 @@ static jmethodID method_getObjectProperty;
static jmethodID method_getObjectInfo;
static jmethodID method_getObjectInfo;
static jmethodID method_getObjectFilePath;
static jmethodID method_getObjectFilePath;
static jmethodID method_deleteFile;
static jmethodID method_deleteFile;
static jmethodID method_getObjectReferences;
static jmethodID method_setObjectReferences;
static jfieldID field_context;
static jfieldID field_context;


MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
@@ -98,6 +100,11 @@ public:
    virtual MtpResponseCode         deleteFile(MtpObjectHandle handle);
    virtual MtpResponseCode         deleteFile(MtpObjectHandle handle);


    bool                            getPropertyInfo(MtpObjectProperty property, int& type);
    bool                            getPropertyInfo(MtpObjectProperty property, int& type);

    virtual MtpObjectHandleList*    getObjectReferences(MtpObjectHandle handle);

    virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
                                            MtpObjectHandleList* references);
};
};


MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client)
MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client)
@@ -344,6 +351,37 @@ bool MyMtpDatabase::getPropertyInfo(MtpObjectProperty property, int& type) {
    return false;
    return false;
}
}


MtpObjectHandleList* MyMtpDatabase::getObjectReferences(MtpObjectHandle handle) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectReferences,
                (jint)handle);
    if (!array)
        return NULL;
    MtpObjectHandleList* list = new MtpObjectHandleList();
    jint* handles = env->GetIntArrayElements(array, 0);
    jsize length = env->GetArrayLength(array);
    for (int i = 0; i < length; i++)
        list->push(handles[i]);
   env->ReleaseIntArrayElements(array, handles, 0);
   return list;
}

MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle, MtpObjectHandleList* references) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    int count = references->size();
    jintArray array = env->NewIntArray(count);
    if (!array) {
        LOGE("out of memory in setObjectReferences");
        return false;
    }
    jint* handles = env->GetIntArrayElements(array, 0);
     for (int i = 0; i < count; i++)
        handles[i] = (*references)[i];
    env->ReleaseIntArrayElements(array, handles, 0);
    return env->CallIntMethod(mDatabase, method_setObjectReferences,
                (jint)handle, array);
}

// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------


static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -442,6 +480,16 @@ int register_android_media_MtpDatabase(JNIEnv *env)
        LOGE("Can't find deleteFile");
        LOGE("Can't find deleteFile");
        return -1;
        return -1;
    }
    }
    method_getObjectReferences = env->GetMethodID(clazz, "getObjectReferences", "(I)[I");
    if (method_getObjectReferences == NULL) {
        LOGE("Can't find getObjectReferences");
        return -1;
    }
    method_setObjectReferences = env->GetMethodID(clazz, "setObjectReferences", "(I[I)I");
    if (method_setObjectReferences == NULL) {
        LOGE("Can't find setObjectReferences");
        return -1;
    }
    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
    if (field_context == NULL) {
    if (field_context == NULL) {
        LOGE("Can't find MtpDatabase.mNativeContext");
        LOGE("Can't find MtpDatabase.mNativeContext");
+56 −38
Original line number Original line Diff line number Diff line
@@ -36,6 +36,7 @@ using namespace android;
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------


static jfieldID field_context;
static jfieldID field_context;
static Mutex    sMutex;


// in android_media_MtpDatabase.cpp
// in android_media_MtpDatabase.cpp
extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
@@ -55,61 +56,78 @@ private:
    MtpServer*      mServer;
    MtpServer*      mServer;
    String8         mStoragePath;
    String8         mStoragePath;
    bool            mDone;
    bool            mDone;
    Mutex           mMutex;
    jobject         mJavaServer;


public:
public:
    MtpThread(MtpDatabase* database, const char* storagePath)
    MtpThread(MtpDatabase* database, const char* storagePath, jobject javaServer)
        : mDatabase(database), mServer(NULL), mStoragePath(storagePath), mDone(false)
        : mDatabase(database),
            mServer(NULL),
            mStoragePath(storagePath),
            mDone(false),
            mJavaServer(javaServer)
    {
    {
    }
    }


    virtual bool threadLoop() {
    virtual bool threadLoop() {
        while (1) {
            int fd = open("/dev/mtp_usb", O_RDWR);
            int fd = open("/dev/mtp_usb", O_RDWR);
            printf("open returned %d\n", fd);
            printf("open returned %d\n", fd);
            if (fd < 0) {
            if (fd < 0) {
                LOGE("could not open MTP driver\n");
                LOGE("could not open MTP driver\n");
            return false;
                break;
            }
            }


        mMutex.lock();
            sMutex.lock();
            mServer = new MtpServer(fd, mDatabase, AID_SDCARD_RW, 0664, 0775);
            mServer = new MtpServer(fd, mDatabase, AID_SDCARD_RW, 0664, 0775);
            mServer->addStorage(mStoragePath);
            mServer->addStorage(mStoragePath);
        mMutex.unlock();
            sMutex.unlock();


            LOGD("MtpThread mServer->run");
            LOGD("MtpThread mServer->run");
            mServer->run();
            mServer->run();
            close(fd);
            close(fd);


        mMutex.lock();
            sMutex.lock();
            delete mServer;
            delete mServer;
            mServer = NULL;
            mServer = NULL;
        mMutex.unlock();
            if (mDone)
                goto done;
            sMutex.unlock();
            // wait a bit before retrying
            sleep(1);
        }


        bool done = mDone;
        sMutex.lock();
        if (done)
done:
            delete this;
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        LOGD("threadLoop returning %s", (done ? "false" : "true"));
        env->SetIntField(mJavaServer, field_context, 0);
        return !done;
        env->DeleteGlobalRef(mJavaServer);
        sMutex.unlock();

        LOGD("threadLoop returning");
        return false;
    }
    }


    void setDone() { mDone = true; }
    void setDone() {
        LOGD("setDone");
        mDone = true; 
    }


    void sendObjectAdded(MtpObjectHandle handle) {
    void sendObjectAdded(MtpObjectHandle handle) {
        mMutex.lock();
        sMutex.lock();
        if (mServer)
        if (mServer)
            mServer->sendObjectAdded(handle);
            mServer->sendObjectAdded(handle);
        else
        else
            LOGE("sendObjectAdded called while disconnected\n");
            LOGE("sendObjectAdded called while disconnected\n");
        mMutex.unlock();
        sMutex.unlock();
    }
    }


    void sendObjectRemoved(MtpObjectHandle handle) {
    void sendObjectRemoved(MtpObjectHandle handle) {
        mMutex.lock();
        sMutex.lock();
        if (mServer)
        if (mServer)
            mServer->sendObjectRemoved(handle);
            mServer->sendObjectRemoved(handle);
        else
        else
            LOGE("sendObjectRemoved called while disconnected\n");
            LOGE("sendObjectRemoved called while disconnected\n");
        mMutex.unlock();
        sMutex.unlock();
    }
    }
};
};


@@ -124,7 +142,7 @@ android_media_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, j
    MtpDatabase* database = getMtpDatabase(env, javaDatabase);
    MtpDatabase* database = getMtpDatabase(env, javaDatabase);
    const char *storagePathStr = env->GetStringUTFChars(storagePath, NULL);
    const char *storagePathStr = env->GetStringUTFChars(storagePath, NULL);


    MtpThread* thread = new MtpThread(database, storagePathStr);
    MtpThread* thread = new MtpThread(database, storagePathStr, env->NewGlobalRef(thiz));
    env->SetIntField(thiz, field_context, (int)thread);
    env->SetIntField(thiz, field_context, (int)thread);


    env->ReleaseStringUTFChars(storagePath, storagePathStr);
    env->ReleaseStringUTFChars(storagePath, storagePathStr);
@@ -153,11 +171,11 @@ android_media_MtpServer_stop(JNIEnv *env, jobject thiz)
{
{
#ifdef HAVE_ANDROID_OS
#ifdef HAVE_ANDROID_OS
    LOGD("stop\n");
    LOGD("stop\n");
    sMutex.lock();
    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
    if (thread) {
    if (thread)
        thread->setDone();
        thread->setDone();
        env->SetIntField(thiz, field_context, 0);
    sMutex.unlock();
    }
#endif
#endif
}
}


+7 −0
Original line number Original line Diff line number Diff line
@@ -61,7 +61,14 @@ public:
    virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
    virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
                                            MtpString& filePath,
                                            MtpString& filePath,
                                            int64_t& fileLength) = 0;
                                            int64_t& fileLength) = 0;

    virtual MtpResponseCode         deleteFile(MtpObjectHandle handle) = 0;
    virtual MtpResponseCode         deleteFile(MtpObjectHandle handle) = 0;

    virtual MtpObjectHandleList*    getObjectReferences(MtpObjectHandle handle) = 0;

    virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
                                            MtpObjectHandleList* references) = 0;

};
};


}; // namespace android
}; // namespace android
Loading