diff options
author | Marco Nelissen <marcone@google.com> | 2017-11-30 12:46:54 -0800 |
---|---|---|
committer | MSe <mse1969@posteo.de> | 2018-06-08 19:25:54 +0200 |
commit | f567aadb3247d638b326d734cf0432a3b8b7fc06 (patch) | |
tree | a36770ce54c157038727e78722c263fae3da86f2 /src/com/android/providers | |
parent | cb85e5fc1eeca18b41fabf8e2d5e506692a93571 (diff) | |
download | android_packages_providers_MediaProvider-f567aadb3247d638b326d734cf0432a3b8b7fc06.tar.gz android_packages_providers_MediaProvider-f567aadb3247d638b326d734cf0432a3b8b7fc06.tar.bz2 android_packages_providers_MediaProvider-f567aadb3247d638b326d734cf0432a3b8b7fc06.zip |
Rework thumbnail cleanupreplicant-6.0-0004-rc1
Bug: 63766886
Test: ran CTS tests
Change-Id: I1173f67d6fb41494d6d069cc7cfc0c2b0dcc1725
(cherry picked from commit 8a1db2e3fa16bce83856a3a8920f8820fe942fce)
CVE-2018-9379
Diffstat (limited to 'src/com/android/providers')
-rwxr-xr-x | src/com/android/providers/media/MediaProvider.java | 183 |
1 files changed, 178 insertions, 5 deletions
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java index 1f15ab20..77ea719b 100755 --- a/src/com/android/providers/media/MediaProvider.java +++ b/src/com/android/providers/media/MediaProvider.java @@ -108,6 +108,7 @@ import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -2083,7 +2084,7 @@ public class MediaProvider extends ContentProvider { } /** - * This method blocks until thumbnail is ready. + * This method requests a thumbnail and blocks until thumbnail is ready. * * @param thumbUri * @return @@ -2296,7 +2297,7 @@ public class MediaProvider extends ContentProvider { int table = URI_MATCHER.match(uri); List<String> prependArgs = new ArrayList<String>(); - // Log.v(TAG, "query: uri="+uri+", selection="+selection); + //Log.v(TAG, "query: uri="+uri+", selection="+selection); // handle MEDIA_SCANNER before calling getDatabaseForUri() if (table == MEDIA_SCANNER) { if (mMediaScannerVolume == null) { @@ -3716,6 +3717,31 @@ public class MediaProvider extends ContentProvider { MediaScanner.clearMediaPathCache(true /* media */, false /* nomedia */); File nomedia = new File(path); String hiddenroot = nomedia.isDirectory() ? path : nomedia.getParent(); + + // query for images and videos that will be affected + Cursor c = db.query("files", + new String[] {"_id", "media_type"}, + "_data >= ? AND _data < ? AND (media_type=1 OR media_type=3)" + + " AND mini_thumb_magic IS NOT NULL", + new String[] { hiddenroot + "/", hiddenroot + "0"}, + null /* groupBy */, null /* having */, null /* orderBy */); + if(c != null) { + if (c.getCount() != 0) { + Uri imagesUri = Uri.parse("content://media/external/images/media"); + Uri videosUri = Uri.parse("content://media/external/videos/media"); + while (c.moveToNext()) { + // remove thumbnail for image/video + long id = c.getLong(0); + long mediaType = c.getLong(1); + Log.i(TAG, "hiding image " + id + ", removing thumbnail"); + removeThumbnailFor(mediaType == FileColumns.MEDIA_TYPE_IMAGE ? + imagesUri : videosUri, db, id); + } + } + IoUtils.closeQuietly(c); + } + + // set the media type of the affected entries to 0 ContentValues mediatype = new ContentValues(); mediatype.put("media_type", 0); int numrows = db.update("files", mediatype, @@ -4039,6 +4065,7 @@ public class MediaProvider extends ContentProvider { logToDb(database.getWritableDatabase(), msg); } mMediaScannerVolume = null; + pruneThumbnails(); return 1; } @@ -4060,6 +4087,7 @@ public class MediaProvider extends ContentProvider { } } else { final String volumeName = getVolumeName(uri); + final boolean isExternal = "external".equals(volumeName); DatabaseHelper database = getDatabaseForUri(uri); if (database == null) { @@ -4077,9 +4105,12 @@ public class MediaProvider extends ContentProvider { database.mNumQueries++; Cursor c = db.query(sGetTableAndWhereParam.table, sMediaTypeDataId, - sGetTableAndWhereParam.where, whereArgs, null, null, null); + sGetTableAndWhereParam.where, whereArgs, + null /* groupBy */, null /* having */, null /* orderBy */); String [] idvalue = new String[] { "" }; String [] playlistvalues = new String[] { "", "" }; + MiniThumbFile imageMicroThumbs = null; + MiniThumbFile videoMicroThumbs = null; try { while (c.moveToNext()) { final int mediaType = c.getInt(0); @@ -4094,7 +4125,9 @@ public class MediaProvider extends ContentProvider { idvalue[0] = String.valueOf(id); database.mNumQueries++; Cursor cc = db.query("thumbnails", sDataOnlyColumn, - "image_id=?", idvalue, null, null, null); + "image_id=?", idvalue, + null /* groupBy */, null /* having */, + null /* orderBy */); try { while (cc.moveToNext()) { deleteIfAllowed(uri, cc.getString(0)); @@ -4104,11 +4137,38 @@ public class MediaProvider extends ContentProvider { } finally { IoUtils.closeQuietly(cc); } + if (isExternal) { + if (imageMicroThumbs == null) { + imageMicroThumbs = MiniThumbFile.instance( + Images.Media.EXTERNAL_CONTENT_URI); + } + imageMicroThumbs.eraseMiniThumb(id); + } } else if (mediaType == FileColumns.MEDIA_TYPE_VIDEO) { deleteIfAllowed(uri, data); MediaDocumentsProvider.onMediaStoreDelete(getContext(), volumeName, FileColumns.MEDIA_TYPE_VIDEO, id); + idvalue[0] = String.valueOf(id); + database.mNumQueries++; + Cursor cc = db.query("videothumbnails", sDataOnlyColumn, + "video_id=?", idvalue, null, null, null); + try { + while (cc.moveToNext()) { + deleteIfAllowed(uri, cc.getString(0)); + } + database.mNumDeletes++; + db.delete("videothumbnails", "video_id=?", idvalue); + } finally { + IoUtils.closeQuietly(cc); + } + if (isExternal) { + if (videoMicroThumbs == null) { + videoMicroThumbs = MiniThumbFile.instance( + Video.Media.EXTERNAL_CONTENT_URI); + } + videoMicroThumbs.eraseMiniThumb(id); + } } else if (mediaType == FileColumns.MEDIA_TYPE_AUDIO) { if (!database.mInternal) { MediaDocumentsProvider.onMediaStoreDelete(getContext(), @@ -4144,6 +4204,12 @@ public class MediaProvider extends ContentProvider { } } finally { IoUtils.closeQuietly(c); + if (imageMicroThumbs != null) { + imageMicroThumbs.deactivate(); + } + if (videoMicroThumbs != null) { + videoMicroThumbs.deactivate(); + } } } } @@ -4216,12 +4282,92 @@ public class MediaProvider extends ContentProvider { throw new UnsupportedOperationException("Unsupported call: " + method); } + + /* + * Clean up all thumbnail files for which the source image or video no longer exists. + * This is called at the end of a media scan. + */ + private void pruneThumbnails() { + Log.v(TAG, "pruneThumbnails "); + + final Uri thumbsUri = Images.Thumbnails.getContentUri("external"); + + // Remove orphan entries in the thumbnails tables + DatabaseHelper helper = getDatabaseForUri(thumbsUri); + SQLiteDatabase db = helper.getWritableDatabase(); + db.execSQL("delete from thumbnails where image_id not in (select _id from images)"); + db.execSQL("delete from videothumbnails where video_id not in (select _id from video)"); + + // Remove cached thumbnails that are no longer referenced by the thumbnails tables + HashSet<String> existingFiles = new HashSet<String>(); + try { + String directory = "/sdcard/DCIM/.thumbnails"; + File dirFile = new File(directory).getCanonicalFile(); + String[] files = dirFile.list(); + if (files == null) + files = new String[0]; + + String dirPath = dirFile.getPath(); + for (int i = 0; i < files.length; i++) { + if (files[i].endsWith(".jpg")) { + String fullPathString = dirPath + "/" + files[i]; + existingFiles.add(fullPathString); + } + } + } catch (IOException e) { + return; + } + + for (String table : new String[] {"thumbnails", "videothumbnails"}) { + Cursor c = db.query(table, new String [] { "_data" }, + null, null, null, null, null); // where clause/args, groupby, having, orderby + if (c != null && c.moveToFirst()) { + do { + String fullPathString = c.getString(0); + existingFiles.remove(fullPathString); + } while (c.moveToNext()); + } + IoUtils.closeQuietly(c); + } + + for (String fileToDelete : existingFiles) { + if (LOCAL_LOGV) + Log.v(TAG, "fileToDelete is " + fileToDelete); + try { + (new File(fileToDelete)).delete(); + } catch (SecurityException ex) { + } + } + + Log.v(TAG, "/pruneDeadThumbnailFiles... "); + } + + private void removeThumbnailFor(Uri uri, SQLiteDatabase db, long id) { + Cursor c = db.rawQuery("select _data from thumbnails where image_id=" + id + + " union all select _data from videothumbnails where video_id=" + id, + null /* selectionArgs */); + if (c != null) { + while (c.moveToNext()) { + String path = c.getString(0); + deleteIfAllowed(uri, path); + } + IoUtils.closeQuietly(c); + db.execSQL("delete from thumbnails where image_id=" + id); + db.execSQL("delete from videothumbnails where video_id=" + id); + } + MiniThumbFile microThumbs = MiniThumbFile.instance(uri); + microThumbs.eraseMiniThumb(id); + microThumbs.deactivate(); + } + @Override public int update(Uri uri, ContentValues initialValues, String userWhere, String[] whereArgs) { uri = safeUncanonicalize(uri); int count; - // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues); + //Log.v(TAG, "update for uri=" + uri + ", initValues=" + initialValues + + // ", where=" + userWhere + ", args=" + Arrays.toString(whereArgs) + " caller:" + + // Binder.getCallingPid()); int match = URI_MATCHER.match(uri); DatabaseHelper helper = getDatabaseForUri(uri); if (helper == null) { @@ -4241,6 +4387,33 @@ public class MediaProvider extends ContentProvider { synchronized (sGetTableAndWhereParam) { getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam); + // if the media type is being changed, check if it's being changed from image or video + // to something else + if (initialValues.containsKey(FileColumns.MEDIA_TYPE)) { + long newMediaType = initialValues.getAsLong(FileColumns.MEDIA_TYPE); + helper.mNumQueries++; + Cursor cursor = db.query(sGetTableAndWhereParam.table, sMediaTableColumns, + sGetTableAndWhereParam.where, whereArgs, null, null, null); + try { + while (cursor != null && cursor.moveToNext()) { + long curMediaType = cursor.getLong(1); + if (curMediaType == FileColumns.MEDIA_TYPE_IMAGE && + newMediaType != FileColumns.MEDIA_TYPE_IMAGE) { + Log.i(TAG, "need to remove image thumbnail for id " + cursor.getString(0)); + removeThumbnailFor(Images.Media.EXTERNAL_CONTENT_URI, + db, cursor.getLong(0)); + } else if (curMediaType == FileColumns.MEDIA_TYPE_VIDEO && + newMediaType != FileColumns.MEDIA_TYPE_VIDEO) { + Log.i(TAG, "need to remove video thumbnail for id " + cursor.getString(0)); + removeThumbnailFor(Video.Media.EXTERNAL_CONTENT_URI, + db, cursor.getLong(0)); + } + } + } finally { + IoUtils.closeQuietly(cursor); + } + } + // special case renaming directories via MTP. // in this case we must update all paths in the database with // the directory name as a prefix |