summaryrefslogtreecommitdiffstats
path: root/src/com/android/providers
diff options
context:
space:
mode:
authorMarco Nelissen <marcone@google.com>2017-11-30 12:46:54 -0800
committerMSe <mse1969@posteo.de>2018-06-08 19:25:54 +0200
commitf567aadb3247d638b326d734cf0432a3b8b7fc06 (patch)
treea36770ce54c157038727e78722c263fae3da86f2 /src/com/android/providers
parentcb85e5fc1eeca18b41fabf8e2d5e506692a93571 (diff)
downloadandroid_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-xsrc/com/android/providers/media/MediaProvider.java183
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