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

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

Merge changes I28b846a3,Ifd9b48cb

* changes:
  MediaScanner: Fix problems with scanner non-file objects from the database
  MTP: Delete all files and subdirectories when deleting directories.
parents 540aada3 d7456c65
Loading
Loading
Loading
Loading
+62 −38
Original line number Diff line number Diff line
@@ -110,15 +110,28 @@ public class MediaScanner

    private final static String TAG = "MediaScanner";

    private static final String[] PRESCAN_PROJECTION = new String[] {
    private static final String[] FILES_PRESCAN_PROJECTION = new String[] {
            Files.FileColumns._ID, // 0
            Files.FileColumns.DATA, // 1
            Files.FileColumns.DATE_MODIFIED, // 2
            Files.FileColumns.FORMAT, // 2
            Files.FileColumns.DATE_MODIFIED, // 3
    };

    private static final int PRESCAN_ID_COLUMN_INDEX = 0;
    private static final int PRESCAN_PATH_COLUMN_INDEX = 1;
    private static final int PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 2;
    private static final int FILES_PRESCAN_ID_COLUMN_INDEX = 0;
    private static final int FILES_PRESCAN_PATH_COLUMN_INDEX = 1;
    private static final int FILES_PRESCAN_FORMAT_COLUMN_INDEX = 2;
    private static final int FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 3;

    private static final String[] MEDIA_PRESCAN_PROJECTION = new String[] {
            MediaStore.MediaColumns._ID, // 0
            MediaStore.MediaColumns.DATA, // 1
            MediaStore.MediaColumns.DATE_MODIFIED, // 2
    };

    private static final int MEDIA_PRESCAN_ID_COLUMN_INDEX = 0;
    private static final int MEDIA_PRESCAN_PATH_COLUMN_INDEX = 1;
    private static final int MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 2;


    private static final String[] PLAYLIST_MEMBERS_PROJECTION = new String[] {
            Audio.Playlists.Members.PLAYLIST_ID, // 0
@@ -316,14 +329,16 @@ public class MediaScanner
        long mRowId;
        String mPath;
        long mLastModified;
        int mFormat;
        boolean mSeenInFileSystem;
        boolean mLastModifiedChanged;

        FileCacheEntry(Uri tableUri, long rowId, String path, long lastModified) {
        FileCacheEntry(Uri tableUri, long rowId, String path, long lastModified, int format) {
            mTableUri = tableUri;
            mRowId = rowId;
            mPath = path;
            mLastModified = lastModified;
            mFormat = format;
            mSeenInFileSystem = false;
            mLastModifiedChanged = false;
        }
@@ -444,7 +459,7 @@ public class MediaScanner
                } else {
                    tableUri = mFilesUri;
                }
                entry = new FileCacheEntry(tableUri, 0, path, 0);
                entry = new FileCacheEntry(tableUri, 0, path, 0, 0);
                mFileCache.put(key, entry);
            }
            entry.mSeenInFileSystem = true;
@@ -884,13 +899,15 @@ public class MediaScanner
            if (prescanFiles) {
                // First read existing files from the files table

                c = mMediaProvider.query(mFilesUri, PRESCAN_PROJECTION, where, selectionArgs, null);
                c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
                        where, selectionArgs, null);

                if (c != null) {
                    while (c.moveToNext()) {
                        long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
                        String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
                        long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
                        long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
                        String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
                        int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
                        long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);

                        // Only consider entries with absolute path names.
                        // This allows storing URIs in the database without the
@@ -902,7 +919,7 @@ public class MediaScanner
                            }

                            FileCacheEntry entry = new FileCacheEntry(mFilesUri, rowId, path,
                                    lastModified);
                                    lastModified, format);
                            mFileCache.put(key, entry);
                        }
                    }
@@ -912,12 +929,13 @@ public class MediaScanner
            }

            // Read existing files from the audio table and update FileCacheEntry
            c = mMediaProvider.query(mAudioUri, PRESCAN_PROJECTION, where, selectionArgs, null);
            c = mMediaProvider.query(mAudioUri, MEDIA_PRESCAN_PROJECTION,
                    where, selectionArgs, null);
            if (c != null) {
                while (c.moveToNext()) {
                    long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
                    String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
                    long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
                    long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
                    String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
                    long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);

                    // Only consider entries with absolute path names.
                    // This allows storing URIs in the database without the
@@ -930,7 +948,7 @@ public class MediaScanner
                        FileCacheEntry entry = mFileCache.get(path);
                        if (entry == null) {
                            mFileCache.put(key, new FileCacheEntry(mAudioUri, rowId, path,
                                    lastModified));
                                    lastModified, 0));
                        } else {
                            // update the entry
                            entry.mTableUri = mAudioUri;
@@ -943,12 +961,13 @@ public class MediaScanner
            }

            // Read existing files from the video table and update FileCacheEntry
            c = mMediaProvider.query(mVideoUri, PRESCAN_PROJECTION, where, selectionArgs, null);
            c = mMediaProvider.query(mVideoUri, MEDIA_PRESCAN_PROJECTION,
                    where, selectionArgs, null);
            if (c != null) {
                while (c.moveToNext()) {
                    long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
                    String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
                    long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
                    long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
                    String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
                    long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);

                    // Only consider entries with absolute path names.
                    // This allows storing URIs in the database without the
@@ -961,7 +980,7 @@ public class MediaScanner
                        FileCacheEntry entry = mFileCache.get(path);
                        if (entry == null) {
                            mFileCache.put(key, new FileCacheEntry(mVideoUri, rowId, path,
                                    lastModified));
                                    lastModified, 0));
                        } else {
                            // update the entry
                            entry.mTableUri = mVideoUri;
@@ -974,12 +993,13 @@ public class MediaScanner
            }

            // Read existing files from the video table and update FileCacheEntry
            c = mMediaProvider.query(mImagesUri, PRESCAN_PROJECTION, where, selectionArgs, null);
            c = mMediaProvider.query(mImagesUri, MEDIA_PRESCAN_PROJECTION,
                    where, selectionArgs, null);
            if (c != null) {
                while (c.moveToNext()) {
                    long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
                    String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
                    long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
                    long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
                    String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
                    long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);

                    // Only consider entries with absolute path names.
                    // This allows storing URIs in the database without the
@@ -992,7 +1012,7 @@ public class MediaScanner
                        FileCacheEntry entry = mFileCache.get(path);
                        if (entry == null) {
                            mFileCache.put(key, new FileCacheEntry(mImagesUri, rowId, path,
                                    lastModified));
                                    lastModified, 0));
                        } else {
                            // update the entry
                            entry.mTableUri = mImagesUri;
@@ -1006,13 +1026,13 @@ public class MediaScanner

            if (mProcessPlaylists) {
                // Read existing files from the playlists table and update FileCacheEntry
                c = mMediaProvider.query(mPlaylistsUri, PRESCAN_PROJECTION, where,
                                            selectionArgs, null);
                c = mMediaProvider.query(mPlaylistsUri, MEDIA_PRESCAN_PROJECTION,
                        where, selectionArgs, null);
                if (c != null) {
                    while (c.moveToNext()) {
                        long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
                        String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
                        long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
                        long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
                        String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
                        long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);

                        // Only consider entries with absolute path names.
                        // This allows storing URIs in the database without the
@@ -1025,7 +1045,7 @@ public class MediaScanner
                            FileCacheEntry entry = mFileCache.get(path);
                            if (entry == null) {
                                mFileCache.put(key, new FileCacheEntry(mPlaylistsUri, rowId, path,
                                        lastModified));
                                        lastModified, 0));
                            } else {
                                // update the entry
                                entry.mTableUri = mPlaylistsUri;
@@ -1109,12 +1129,14 @@ public class MediaScanner
            // remove database entries for files that no longer exist.
            boolean fileMissing = false;

            if (!entry.mSeenInFileSystem) {
                if (inScanDirectory(path, directories)) {
            if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) {
                if (entry.mFormat != MtpConstants.FORMAT_ASSOCIATION &&
                        inScanDirectory(path, directories)) {
                    // we didn't see this file in the scan directory.
                    fileMissing = true;
                } else {
                    // the file is outside of our scan directory,
                    // the file actually a directory or other abstract object
                    // or is outside of our scan directory,
                    // so we need to check for file existence here.
                    File testFile = new File(path);
                    if (!testFile.exists()) {
@@ -1134,9 +1156,11 @@ public class MediaScanner
                    ContentValues values = new ContentValues();
                    values.put(MediaStore.Audio.Playlists.DATA, "");
                    values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, 0);
                    mMediaProvider.update(ContentUris.withAppendedId(mPlaylistsUri, entry.mRowId), values, null, null);
                    mMediaProvider.update(ContentUris.withAppendedId(mPlaylistsUri, entry.mRowId),
                            values, null, null);
                } else {
                    mMediaProvider.delete(ContentUris.withAppendedId(mFilesUri, entry.mRowId), null, null);
                    mMediaProvider.delete(ContentUris.withAppendedId(mFilesUri, entry.mRowId),
                            null, null);
                    iterator.remove();
                }
            }
+22 −0
Original line number Diff line number Diff line
@@ -138,6 +138,28 @@ public final class MtpConstants {
    public static final int FORMAT_ABSTRACT_CONTACT = 0xBB81;
    public static final int FORMAT_VCARD_2 = 0xBB82;

    public static boolean isAbstractObject(int format) {
        switch (format) {
            case FORMAT_ABSTRACT_MULTIMEDIA_ALBUM:
            case FORMAT_ABSTRACT_IMAGE_ALBUM:
            case FORMAT_ABSTRACT_AUDIO_ALBUM:
            case FORMAT_ABSTRACT_VIDEO_ALBUM:
            case FORMAT_ABSTRACT_AV_PLAYLIST:
            case FORMAT_ABSTRACT_CONTACT_GROUP:
            case FORMAT_ABSTRACT_MESSAGE_FOLDER:
            case FORMAT_ABSTRACT_CHAPTERED_PRODUCTION:
            case FORMAT_ABSTRACT_AUDIO_PLAYLIST:
            case FORMAT_ABSTRACT_VIDEO_PLAYLIST:
            case FORMAT_ABSTRACT_MEDIACAST:
            case FORMAT_ABSTRACT_DOCUMENT:
            case FORMAT_ABSTRACT_MESSSAGE:
            case FORMAT_ABSTRACT_CONTACT:
                return true;
            default:
                return false;
        }
    }

    // MTP object properties
    public static final int PROPERTY_STORAGE_ID = 0xDC01;
    public static final int PROPERTY_OBJECT_FORMAT = 0xDC02;
+16 −2
Original line number Diff line number Diff line
@@ -478,12 +478,26 @@ public class MtpDatabase {
        }
    }

    private int deleteRecursive(int handle) throws RemoteException {
        int[] children = getObjectList(0 /* storageID */, 0 /* format */, handle);
        Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
        // delete parent first, to avoid potential infinite recursion
        int count = mMediaProvider.delete(uri, null, null);
        if (count == 1) {
            if (children != null) {
                for (int i = 0; i < children.length; i++) {
                    count += deleteRecursive(children[i]);
                }
            }
        }
        return count;
    }

    private int deleteFile(int handle) {
        Log.d(TAG, "deleteFile: " + handle);
        mDatabaseModified = true;
        Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
        try {
            if (mMediaProvider.delete(uri, null, null) == 1) {
            if (deleteRecursive(handle) > 0) {
                return MtpConstants.RESPONSE_OK;
            } else {
                return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+63 −5
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <dirent.h>

#include <cutils/properties.h>

@@ -686,21 +688,77 @@ done:
    return result;
}

static void deleteRecursive(const char* path) {
    char pathbuf[PATH_MAX];
    int pathLength = strlen(path);
    if (pathLength >= sizeof(pathbuf) - 1) {
        LOGE("path too long: %s\n", path);
    }
    strcpy(pathbuf, path);
    if (pathbuf[pathLength - 1] != '/') {
        pathbuf[pathLength++] = '/';
    }
    char* fileSpot = pathbuf + pathLength;
    int pathRemaining = sizeof(pathbuf) - pathLength - 1;

    DIR* dir = opendir(path);
    if (!dir) {
        LOGE("opendir %s failed: %s", path, strerror(errno));
        return;
    }

    struct dirent* entry;
    while ((entry = readdir(dir))) {
        const char* name = entry->d_name;

        // ignore "." and ".."
        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
            continue;
        }

        int nameLength = strlen(name);
        if (nameLength > pathRemaining) {
            LOGE("path %s/%s too long\n", path, name);
            continue;
        }
        strcpy(fileSpot, name);

        int type = entry->d_type;
        if (entry->d_type == DT_DIR) {
            deleteRecursive(pathbuf);
            rmdir(pathbuf);
        } else {
            unlink(pathbuf);
        }
    }
}

static void deletePath(const char* path) {
    struct stat statbuf;
    if (stat(path, &statbuf) == 0) {
        if (S_ISDIR(statbuf.st_mode)) {
            deleteRecursive(path);
            rmdir(path);
        } else {
            unlink(path);
        }
    } else {
        LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
    }
}

MtpResponseCode MtpServer::doDeleteObject() {
    MtpObjectHandle handle = mRequest.getParameter(1);
    MtpObjectFormat format = mRequest.getParameter(1);
    MtpObjectFormat format = mRequest.getParameter(2);
    // FIXME - support deleting all objects if handle is 0xFFFFFFFF
    // FIXME - implement deleting objects by format
    // FIXME - handle non-empty directories

    MtpString filePath;
    int64_t fileLength;
    int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
    if (result == MTP_RESPONSE_OK) {
        LOGV("deleting %s", (const char *)filePath);
        // one of these should work
        rmdir((const char *)filePath);
        unlink((const char *)filePath);
        deletePath((const char *)filePath);
        return mDatabase->deleteFile(handle);
    } else {
        return result;