Loading media/java/android/media/MediaScanner.java +133 −116 Original line number Original line Diff line number Diff line Loading @@ -62,6 +62,9 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Locale; import libcore.io.ErrnoException; import libcore.io.Libcore; /** /** * Internal service helper that no-one should use directly. * Internal service helper that no-one should use directly. * * Loading Loading @@ -348,20 +351,18 @@ public class MediaScanner private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options(); private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options(); private static class FileCacheEntry { private static class FileEntry { long mRowId; long mRowId; String mPath; String mPath; long mLastModified; long mLastModified; int mFormat; int mFormat; boolean mSeenInFileSystem; boolean mLastModifiedChanged; boolean mLastModifiedChanged; FileCacheEntry(long rowId, String path, long lastModified, int format) { FileEntry(long rowId, String path, long lastModified, int format) { mRowId = rowId; mRowId = rowId; mPath = path; mPath = path; mLastModified = lastModified; mLastModified = lastModified; mFormat = format; mFormat = format; mSeenInFileSystem = false; mLastModifiedChanged = false; mLastModifiedChanged = false; } } Loading @@ -373,11 +374,7 @@ public class MediaScanner private MediaInserter mMediaInserter; private MediaInserter mMediaInserter; // hashes file path to FileCacheEntry. private ArrayList<FileEntry> mPlayLists; // path should be lower case if mCaseInsensitivePaths is true private LinkedHashMap<String, FileCacheEntry> mFileCache; private ArrayList<FileCacheEntry> mPlayLists; private DrmManagerClient mDrmManagerClient = null; private DrmManagerClient mDrmManagerClient = null; Loading Loading @@ -432,7 +429,7 @@ public class MediaScanner private int mWidth; private int mWidth; private int mHeight; private int mHeight; public FileCacheEntry beginFile(String path, String mimeType, long lastModified, public FileEntry beginFile(String path, String mimeType, long lastModified, long fileSize, boolean isDirectory, boolean noMedia) { long fileSize, boolean isDirectory, boolean noMedia) { mMimeType = mimeType; mMimeType = mimeType; mFileType = 0; mFileType = 0; Loading Loading @@ -465,11 +462,7 @@ public class MediaScanner } } } } String key = path; FileEntry entry = makeEntryFor(path); if (mCaseInsensitivePaths) { key = path.toLowerCase(); } FileCacheEntry entry = mFileCache.get(key); // add some slack to avoid a rounding error // add some slack to avoid a rounding error long delta = (entry != null) ? (lastModified - entry.mLastModified) : 0; long delta = (entry != null) ? (lastModified - entry.mLastModified) : 0; boolean wasModified = delta > 1 || delta < -1; boolean wasModified = delta > 1 || delta < -1; Loading @@ -477,13 +470,11 @@ public class MediaScanner if (wasModified) { if (wasModified) { entry.mLastModified = lastModified; entry.mLastModified = lastModified; } else { } else { entry = new FileCacheEntry(0, path, lastModified, entry = new FileEntry(0, path, lastModified, (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0)); (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0)); mFileCache.put(key, entry); } } entry.mLastModifiedChanged = true; entry.mLastModifiedChanged = true; } } entry.mSeenInFileSystem = true; if (mProcessPlaylists && MediaFile.isPlayListFileType(mFileType)) { if (mProcessPlaylists && MediaFile.isPlayListFileType(mFileType)) { mPlayLists.add(entry); mPlayLists.add(entry); Loading Loading @@ -525,7 +516,7 @@ public class MediaScanner Uri result = null; Uri result = null; // long t1 = System.currentTimeMillis(); // long t1 = System.currentTimeMillis(); try { try { FileCacheEntry entry = beginFile(path, mimeType, lastModified, FileEntry entry = beginFile(path, mimeType, lastModified, fileSize, isDirectory, noMedia); fileSize, isDirectory, noMedia); // rescan for metadata if file was modified since last scan // rescan for metadata if file was modified since last scan if (entry != null && (entry.mLastModifiedChanged || scanAlways)) { if (entry != null && (entry.mLastModifiedChanged || scanAlways)) { Loading Loading @@ -778,7 +769,7 @@ public class MediaScanner return map; return map; } } private Uri endFile(FileCacheEntry entry, boolean ringtones, boolean notifications, private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications, boolean alarms, boolean music, boolean podcasts) boolean alarms, boolean music, boolean podcasts) throws RemoteException { throws RemoteException { // update database // update database Loading Loading @@ -1028,55 +1019,94 @@ public class MediaScanner String where = null; String where = null; String[] selectionArgs = null; String[] selectionArgs = null; if (mFileCache == null) { mFileCache = new LinkedHashMap<String, FileCacheEntry>(); } else { mFileCache.clear(); } if (mPlayLists == null) { if (mPlayLists == null) { mPlayLists = new ArrayList<FileCacheEntry>(); mPlayLists = new ArrayList<FileEntry>(); } else { } else { mPlayLists.clear(); mPlayLists.clear(); } } if (filePath != null) { if (filePath != null) { // query for only one file // query for only one file where = Files.FileColumns.DATA + "=?"; where = MediaStore.Files.FileColumns._ID + ">?" + selectionArgs = new String[] { filePath }; " AND " + Files.FileColumns.DATA + "=?"; selectionArgs = new String[] { "", filePath }; } else { where = MediaStore.Files.FileColumns._ID + ">?"; selectionArgs = new String[] { "" }; } } // Tell the provider to not delete the file. // If the file is truly gone the delete is unnecessary, and we want to avoid // accidentally deleting files that are really there (this may happen if the // filesystem is mounted and unmounted while the scanner is running). Uri.Builder builder = mFilesUri.buildUpon(); builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false"); MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build()); // Build the list of files from the content provider // Build the list of files from the content provider try { try { if (prescanFiles) { if (prescanFiles) { // First read existing files from the files table // First read existing files from the files table. // Because we'll be deleting entries for missing files as we go, c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION, // we need to query the database in small batches, to avoid problems where, selectionArgs, null, null); // with CursorWindow positioning. long lastId = Long.MIN_VALUE; Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build(); mWasEmptyPriorToScan = true; while (true) { selectionArgs[0] = "" + lastId; if (c != null) { if (c != null) { mWasEmptyPriorToScan = c.getCount() == 0; c.close(); c = null; } c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION, where, selectionArgs, MediaStore.Files.FileColumns._ID, null); if (c == null) { break; } int num = c.getCount(); if (num == 0) { break; } mWasEmptyPriorToScan = false; while (c.moveToNext()) { while (c.moveToNext()) { long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX); long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX); String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX); String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX); int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX); int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX); long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX); long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX); lastId = rowId; // Only consider entries with absolute path names. // Only consider entries with absolute path names. // This allows storing URIs in the database without the // This allows storing URIs in the database without the // media scanner removing them. // media scanner removing them. if (path != null && path.startsWith("/")) { if (path != null && path.startsWith("/")) { String key = path; boolean exists = false; if (mCaseInsensitivePaths) { try { key = path.toLowerCase(); exists = Libcore.os.access(path, libcore.io.OsConstants.F_OK); } catch (ErrnoException e1) { } } if (!exists && !MtpConstants.isAbstractObject(format)) { // do not delete missing playlists, since they may have been // modified by the user. // The user can delete them in the media player instead. // instead, clear the path and lastModified fields in the row MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType); FileCacheEntry entry = new FileCacheEntry(rowId, path, if (!MediaFile.isPlayListFileType(fileType)) { lastModified, format); deleter.delete(rowId); mFileCache.put(key, entry); if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) { deleter.flush(); String parent = new File(path).getParent(); mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null); } } } } } } } c.close(); c = null; } } } } } } Loading @@ -1084,6 +1114,7 @@ public class MediaScanner if (c != null) { if (c != null) { c.close(); c.close(); } } deleter.flush(); } } // compute original size of images // compute original size of images Loading Loading @@ -1186,57 +1217,6 @@ public class MediaScanner } } private void postscan(String[] directories) throws RemoteException { private void postscan(String[] directories) throws RemoteException { Iterator<FileCacheEntry> iterator = mFileCache.values().iterator(); // Tell the provider to not delete the file. // If the file is truly gone the delete is unnecessary, and we want to avoid // accidentally deleting files that are really there (this may happen if the // filesystem is mounted and unmounted while the scanner is running). Uri.Builder builder = mFilesUri.buildUpon(); builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false"); MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build()); while (iterator.hasNext()) { FileCacheEntry entry = iterator.next(); String path = entry.mPath; // remove database entries for files that no longer exist. boolean fileMissing = false; if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) { if (inScanDirectory(path, directories)) { // we didn't see this file in the scan directory. fileMissing = true; } else { // 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()) { fileMissing = true; } } } if (fileMissing) { // do not delete missing playlists, since they may have been modified by the user. // the user can delete them in the media player instead. // instead, clear the path and lastModified fields in the row MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType); if (!MediaFile.isPlayListFileType(fileType)) { deleter.delete(entry.mRowId); iterator.remove(); if (entry.mPath.toLowerCase(Locale.US).endsWith("/.nomedia")) { deleter.flush(); File f = new File(path); mMediaProvider.call(MediaStore.UNHIDE_CALL, f.getParent(), null); } } } } deleter.flush(); // handle playlists last, after we know what media files are on the storage. // handle playlists last, after we know what media files are on the storage. if (mProcessPlaylists) { if (mProcessPlaylists) { Loading @@ -1248,7 +1228,6 @@ public class MediaScanner // allow GC to clean up // allow GC to clean up mPlayLists = null; mPlayLists = null; mFileCache = null; mMediaProvider = null; mMediaProvider = null; } } Loading Loading @@ -1422,11 +1401,7 @@ public class MediaScanner // build file cache so we can look up tracks in the playlist // build file cache so we can look up tracks in the playlist prescan(null, true); prescan(null, true); String key = path; FileEntry entry = makeEntryFor(path); if (mCaseInsensitivePaths) { key = path.toLowerCase(); } FileCacheEntry entry = mFileCache.get(key); if (entry != null) { if (entry != null) { processPlayList(entry); processPlayList(entry); } } Loading @@ -1445,6 +1420,37 @@ public class MediaScanner } } } } FileEntry makeEntryFor(String path) { String key = path; String where; String[] selectionArgs; if (mCaseInsensitivePaths) { where = Files.FileColumns.DATA + " LIKE ?"; selectionArgs = new String[] { path }; } else { where = Files.FileColumns.DATA + "=?"; selectionArgs = new String[] { path }; } Cursor c = null; try { c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION, where, selectionArgs, null, null); if (c.moveToNext()) { long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX); int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX); long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX); return new FileEntry(rowId, path, lastModified, format); } } catch (RemoteException e) { } finally { if (c != null) { c.close(); } } return null; } // returns the number of matching file/directory names, starting from the right // returns the number of matching file/directory names, starting from the right private int matchPaths(String path1, String path2) { private int matchPaths(String path1, String path2) { int result = 0; int result = 0; Loading Loading @@ -1495,27 +1501,38 @@ public class MediaScanner //FIXME - should we look for "../" within the path? //FIXME - should we look for "../" within the path? // best matching MediaFile for the play list entry // best matching MediaFile for the play list entry FileCacheEntry bestMatch = null; FileEntry bestMatch = null; // number of rightmost file/directory names for bestMatch // number of rightmost file/directory names for bestMatch int bestMatchLength = 0; int bestMatchLength = 0; Iterator<FileCacheEntry> iterator = mFileCache.values().iterator(); Cursor c = null; while (iterator.hasNext()) { try { FileCacheEntry cacheEntry = iterator.next(); c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION, String path = cacheEntry.mPath; null, null, null, null); } catch (RemoteException e1) { } if (c != null) { while (c.moveToNext()) { 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); if (path.equalsIgnoreCase(entry)) { if (path.equalsIgnoreCase(entry)) { bestMatch = cacheEntry; bestMatch = new FileEntry(rowId, path, lastModified, format); break; // don't bother continuing search break; // don't bother continuing search } } int matchLength = matchPaths(path, entry); int matchLength = matchPaths(path, entry); if (matchLength > bestMatchLength) { if (matchLength > bestMatchLength) { bestMatch = cacheEntry; bestMatch = new FileEntry(rowId, path, lastModified, format); bestMatchLength = matchLength; bestMatchLength = matchLength; } } } } c.close(); } if (bestMatch == null) { if (bestMatch == null) { return false; return false; Loading @@ -1524,7 +1541,7 @@ public class MediaScanner try { try { // check rowid is set. Rowid may be missing if it is inserted by bulkInsert(). // check rowid is set. Rowid may be missing if it is inserted by bulkInsert(). if (bestMatch.mRowId == 0) { if (bestMatch.mRowId == 0) { Cursor c = mMediaProvider.query(mAudioUri, ID_PROJECTION, c = mMediaProvider.query(mAudioUri, ID_PROJECTION, MediaStore.Files.FileColumns.DATA + "=?", MediaStore.Files.FileColumns.DATA + "=?", new String[] { bestMatch.mPath }, null, null); new String[] { bestMatch.mPath }, null, null); if (c != null) { if (c != null) { Loading Loading @@ -1677,7 +1694,7 @@ public class MediaScanner } } } } private void processPlayList(FileCacheEntry entry) throws RemoteException { private void processPlayList(FileEntry entry) throws RemoteException { String path = entry.mPath; String path = entry.mPath; ContentValues values = new ContentValues(); ContentValues values = new ContentValues(); int lastSlash = path.lastIndexOf('/'); int lastSlash = path.lastIndexOf('/'); Loading Loading @@ -1728,9 +1745,9 @@ public class MediaScanner } } private void processPlayLists() throws RemoteException { private void processPlayLists() throws RemoteException { Iterator<FileCacheEntry> iterator = mPlayLists.iterator(); Iterator<FileEntry> iterator = mPlayLists.iterator(); while (iterator.hasNext()) { while (iterator.hasNext()) { FileCacheEntry entry = iterator.next(); FileEntry entry = iterator.next(); // only process playlist files if they are new or have been modified since the last scan // only process playlist files if they are new or have been modified since the last scan if (entry.mLastModifiedChanged) { if (entry.mLastModifiedChanged) { processPlayList(entry); processPlayList(entry); Loading Loading
media/java/android/media/MediaScanner.java +133 −116 Original line number Original line Diff line number Diff line Loading @@ -62,6 +62,9 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Locale; import libcore.io.ErrnoException; import libcore.io.Libcore; /** /** * Internal service helper that no-one should use directly. * Internal service helper that no-one should use directly. * * Loading Loading @@ -348,20 +351,18 @@ public class MediaScanner private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options(); private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options(); private static class FileCacheEntry { private static class FileEntry { long mRowId; long mRowId; String mPath; String mPath; long mLastModified; long mLastModified; int mFormat; int mFormat; boolean mSeenInFileSystem; boolean mLastModifiedChanged; boolean mLastModifiedChanged; FileCacheEntry(long rowId, String path, long lastModified, int format) { FileEntry(long rowId, String path, long lastModified, int format) { mRowId = rowId; mRowId = rowId; mPath = path; mPath = path; mLastModified = lastModified; mLastModified = lastModified; mFormat = format; mFormat = format; mSeenInFileSystem = false; mLastModifiedChanged = false; mLastModifiedChanged = false; } } Loading @@ -373,11 +374,7 @@ public class MediaScanner private MediaInserter mMediaInserter; private MediaInserter mMediaInserter; // hashes file path to FileCacheEntry. private ArrayList<FileEntry> mPlayLists; // path should be lower case if mCaseInsensitivePaths is true private LinkedHashMap<String, FileCacheEntry> mFileCache; private ArrayList<FileCacheEntry> mPlayLists; private DrmManagerClient mDrmManagerClient = null; private DrmManagerClient mDrmManagerClient = null; Loading Loading @@ -432,7 +429,7 @@ public class MediaScanner private int mWidth; private int mWidth; private int mHeight; private int mHeight; public FileCacheEntry beginFile(String path, String mimeType, long lastModified, public FileEntry beginFile(String path, String mimeType, long lastModified, long fileSize, boolean isDirectory, boolean noMedia) { long fileSize, boolean isDirectory, boolean noMedia) { mMimeType = mimeType; mMimeType = mimeType; mFileType = 0; mFileType = 0; Loading Loading @@ -465,11 +462,7 @@ public class MediaScanner } } } } String key = path; FileEntry entry = makeEntryFor(path); if (mCaseInsensitivePaths) { key = path.toLowerCase(); } FileCacheEntry entry = mFileCache.get(key); // add some slack to avoid a rounding error // add some slack to avoid a rounding error long delta = (entry != null) ? (lastModified - entry.mLastModified) : 0; long delta = (entry != null) ? (lastModified - entry.mLastModified) : 0; boolean wasModified = delta > 1 || delta < -1; boolean wasModified = delta > 1 || delta < -1; Loading @@ -477,13 +470,11 @@ public class MediaScanner if (wasModified) { if (wasModified) { entry.mLastModified = lastModified; entry.mLastModified = lastModified; } else { } else { entry = new FileCacheEntry(0, path, lastModified, entry = new FileEntry(0, path, lastModified, (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0)); (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0)); mFileCache.put(key, entry); } } entry.mLastModifiedChanged = true; entry.mLastModifiedChanged = true; } } entry.mSeenInFileSystem = true; if (mProcessPlaylists && MediaFile.isPlayListFileType(mFileType)) { if (mProcessPlaylists && MediaFile.isPlayListFileType(mFileType)) { mPlayLists.add(entry); mPlayLists.add(entry); Loading Loading @@ -525,7 +516,7 @@ public class MediaScanner Uri result = null; Uri result = null; // long t1 = System.currentTimeMillis(); // long t1 = System.currentTimeMillis(); try { try { FileCacheEntry entry = beginFile(path, mimeType, lastModified, FileEntry entry = beginFile(path, mimeType, lastModified, fileSize, isDirectory, noMedia); fileSize, isDirectory, noMedia); // rescan for metadata if file was modified since last scan // rescan for metadata if file was modified since last scan if (entry != null && (entry.mLastModifiedChanged || scanAlways)) { if (entry != null && (entry.mLastModifiedChanged || scanAlways)) { Loading Loading @@ -778,7 +769,7 @@ public class MediaScanner return map; return map; } } private Uri endFile(FileCacheEntry entry, boolean ringtones, boolean notifications, private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications, boolean alarms, boolean music, boolean podcasts) boolean alarms, boolean music, boolean podcasts) throws RemoteException { throws RemoteException { // update database // update database Loading Loading @@ -1028,55 +1019,94 @@ public class MediaScanner String where = null; String where = null; String[] selectionArgs = null; String[] selectionArgs = null; if (mFileCache == null) { mFileCache = new LinkedHashMap<String, FileCacheEntry>(); } else { mFileCache.clear(); } if (mPlayLists == null) { if (mPlayLists == null) { mPlayLists = new ArrayList<FileCacheEntry>(); mPlayLists = new ArrayList<FileEntry>(); } else { } else { mPlayLists.clear(); mPlayLists.clear(); } } if (filePath != null) { if (filePath != null) { // query for only one file // query for only one file where = Files.FileColumns.DATA + "=?"; where = MediaStore.Files.FileColumns._ID + ">?" + selectionArgs = new String[] { filePath }; " AND " + Files.FileColumns.DATA + "=?"; selectionArgs = new String[] { "", filePath }; } else { where = MediaStore.Files.FileColumns._ID + ">?"; selectionArgs = new String[] { "" }; } } // Tell the provider to not delete the file. // If the file is truly gone the delete is unnecessary, and we want to avoid // accidentally deleting files that are really there (this may happen if the // filesystem is mounted and unmounted while the scanner is running). Uri.Builder builder = mFilesUri.buildUpon(); builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false"); MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build()); // Build the list of files from the content provider // Build the list of files from the content provider try { try { if (prescanFiles) { if (prescanFiles) { // First read existing files from the files table // First read existing files from the files table. // Because we'll be deleting entries for missing files as we go, c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION, // we need to query the database in small batches, to avoid problems where, selectionArgs, null, null); // with CursorWindow positioning. long lastId = Long.MIN_VALUE; Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build(); mWasEmptyPriorToScan = true; while (true) { selectionArgs[0] = "" + lastId; if (c != null) { if (c != null) { mWasEmptyPriorToScan = c.getCount() == 0; c.close(); c = null; } c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION, where, selectionArgs, MediaStore.Files.FileColumns._ID, null); if (c == null) { break; } int num = c.getCount(); if (num == 0) { break; } mWasEmptyPriorToScan = false; while (c.moveToNext()) { while (c.moveToNext()) { long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX); long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX); String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX); String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX); int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX); int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX); long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX); long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX); lastId = rowId; // Only consider entries with absolute path names. // Only consider entries with absolute path names. // This allows storing URIs in the database without the // This allows storing URIs in the database without the // media scanner removing them. // media scanner removing them. if (path != null && path.startsWith("/")) { if (path != null && path.startsWith("/")) { String key = path; boolean exists = false; if (mCaseInsensitivePaths) { try { key = path.toLowerCase(); exists = Libcore.os.access(path, libcore.io.OsConstants.F_OK); } catch (ErrnoException e1) { } } if (!exists && !MtpConstants.isAbstractObject(format)) { // do not delete missing playlists, since they may have been // modified by the user. // The user can delete them in the media player instead. // instead, clear the path and lastModified fields in the row MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType); FileCacheEntry entry = new FileCacheEntry(rowId, path, if (!MediaFile.isPlayListFileType(fileType)) { lastModified, format); deleter.delete(rowId); mFileCache.put(key, entry); if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) { deleter.flush(); String parent = new File(path).getParent(); mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null); } } } } } } } c.close(); c = null; } } } } } } Loading @@ -1084,6 +1114,7 @@ public class MediaScanner if (c != null) { if (c != null) { c.close(); c.close(); } } deleter.flush(); } } // compute original size of images // compute original size of images Loading Loading @@ -1186,57 +1217,6 @@ public class MediaScanner } } private void postscan(String[] directories) throws RemoteException { private void postscan(String[] directories) throws RemoteException { Iterator<FileCacheEntry> iterator = mFileCache.values().iterator(); // Tell the provider to not delete the file. // If the file is truly gone the delete is unnecessary, and we want to avoid // accidentally deleting files that are really there (this may happen if the // filesystem is mounted and unmounted while the scanner is running). Uri.Builder builder = mFilesUri.buildUpon(); builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false"); MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build()); while (iterator.hasNext()) { FileCacheEntry entry = iterator.next(); String path = entry.mPath; // remove database entries for files that no longer exist. boolean fileMissing = false; if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) { if (inScanDirectory(path, directories)) { // we didn't see this file in the scan directory. fileMissing = true; } else { // 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()) { fileMissing = true; } } } if (fileMissing) { // do not delete missing playlists, since they may have been modified by the user. // the user can delete them in the media player instead. // instead, clear the path and lastModified fields in the row MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType); if (!MediaFile.isPlayListFileType(fileType)) { deleter.delete(entry.mRowId); iterator.remove(); if (entry.mPath.toLowerCase(Locale.US).endsWith("/.nomedia")) { deleter.flush(); File f = new File(path); mMediaProvider.call(MediaStore.UNHIDE_CALL, f.getParent(), null); } } } } deleter.flush(); // handle playlists last, after we know what media files are on the storage. // handle playlists last, after we know what media files are on the storage. if (mProcessPlaylists) { if (mProcessPlaylists) { Loading @@ -1248,7 +1228,6 @@ public class MediaScanner // allow GC to clean up // allow GC to clean up mPlayLists = null; mPlayLists = null; mFileCache = null; mMediaProvider = null; mMediaProvider = null; } } Loading Loading @@ -1422,11 +1401,7 @@ public class MediaScanner // build file cache so we can look up tracks in the playlist // build file cache so we can look up tracks in the playlist prescan(null, true); prescan(null, true); String key = path; FileEntry entry = makeEntryFor(path); if (mCaseInsensitivePaths) { key = path.toLowerCase(); } FileCacheEntry entry = mFileCache.get(key); if (entry != null) { if (entry != null) { processPlayList(entry); processPlayList(entry); } } Loading @@ -1445,6 +1420,37 @@ public class MediaScanner } } } } FileEntry makeEntryFor(String path) { String key = path; String where; String[] selectionArgs; if (mCaseInsensitivePaths) { where = Files.FileColumns.DATA + " LIKE ?"; selectionArgs = new String[] { path }; } else { where = Files.FileColumns.DATA + "=?"; selectionArgs = new String[] { path }; } Cursor c = null; try { c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION, where, selectionArgs, null, null); if (c.moveToNext()) { long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX); int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX); long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX); return new FileEntry(rowId, path, lastModified, format); } } catch (RemoteException e) { } finally { if (c != null) { c.close(); } } return null; } // returns the number of matching file/directory names, starting from the right // returns the number of matching file/directory names, starting from the right private int matchPaths(String path1, String path2) { private int matchPaths(String path1, String path2) { int result = 0; int result = 0; Loading Loading @@ -1495,27 +1501,38 @@ public class MediaScanner //FIXME - should we look for "../" within the path? //FIXME - should we look for "../" within the path? // best matching MediaFile for the play list entry // best matching MediaFile for the play list entry FileCacheEntry bestMatch = null; FileEntry bestMatch = null; // number of rightmost file/directory names for bestMatch // number of rightmost file/directory names for bestMatch int bestMatchLength = 0; int bestMatchLength = 0; Iterator<FileCacheEntry> iterator = mFileCache.values().iterator(); Cursor c = null; while (iterator.hasNext()) { try { FileCacheEntry cacheEntry = iterator.next(); c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION, String path = cacheEntry.mPath; null, null, null, null); } catch (RemoteException e1) { } if (c != null) { while (c.moveToNext()) { 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); if (path.equalsIgnoreCase(entry)) { if (path.equalsIgnoreCase(entry)) { bestMatch = cacheEntry; bestMatch = new FileEntry(rowId, path, lastModified, format); break; // don't bother continuing search break; // don't bother continuing search } } int matchLength = matchPaths(path, entry); int matchLength = matchPaths(path, entry); if (matchLength > bestMatchLength) { if (matchLength > bestMatchLength) { bestMatch = cacheEntry; bestMatch = new FileEntry(rowId, path, lastModified, format); bestMatchLength = matchLength; bestMatchLength = matchLength; } } } } c.close(); } if (bestMatch == null) { if (bestMatch == null) { return false; return false; Loading @@ -1524,7 +1541,7 @@ public class MediaScanner try { try { // check rowid is set. Rowid may be missing if it is inserted by bulkInsert(). // check rowid is set. Rowid may be missing if it is inserted by bulkInsert(). if (bestMatch.mRowId == 0) { if (bestMatch.mRowId == 0) { Cursor c = mMediaProvider.query(mAudioUri, ID_PROJECTION, c = mMediaProvider.query(mAudioUri, ID_PROJECTION, MediaStore.Files.FileColumns.DATA + "=?", MediaStore.Files.FileColumns.DATA + "=?", new String[] { bestMatch.mPath }, null, null); new String[] { bestMatch.mPath }, null, null); if (c != null) { if (c != null) { Loading Loading @@ -1677,7 +1694,7 @@ public class MediaScanner } } } } private void processPlayList(FileCacheEntry entry) throws RemoteException { private void processPlayList(FileEntry entry) throws RemoteException { String path = entry.mPath; String path = entry.mPath; ContentValues values = new ContentValues(); ContentValues values = new ContentValues(); int lastSlash = path.lastIndexOf('/'); int lastSlash = path.lastIndexOf('/'); Loading Loading @@ -1728,9 +1745,9 @@ public class MediaScanner } } private void processPlayLists() throws RemoteException { private void processPlayLists() throws RemoteException { Iterator<FileCacheEntry> iterator = mPlayLists.iterator(); Iterator<FileEntry> iterator = mPlayLists.iterator(); while (iterator.hasNext()) { while (iterator.hasNext()) { FileCacheEntry entry = iterator.next(); FileEntry entry = iterator.next(); // only process playlist files if they are new or have been modified since the last scan // only process playlist files if they are new or have been modified since the last scan if (entry.mLastModifiedChanged) { if (entry.mLastModifiedChanged) { processPlayList(entry); processPlayList(entry); Loading