From e24609e8f2aada5cb8ac1c3e93141b260e201efe Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 16 Oct 2014 14:40:23 -0700 Subject: Trim stale downloads from third-party apps. Buggy third-party apps can enqueue lots of downloads and then forget to remove them, causing DownloadManager to stop functioning. This change removes any downloads that match _all_ of the following conditions: 1. Download status is in a terminal (non-pending) state, usually a concrete success or failure. 2. Download hasn't been touched in over a week. 3. Download is not visible in UI. Bug: 17785419 Change-Id: Id82752fd6935371c1af682205d35f7ba35169473 --- .../providers/downloads/DownloadIdleService.java | 52 +++++++++++++++++++--- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/src/com/android/providers/downloads/DownloadIdleService.java b/src/com/android/providers/downloads/DownloadIdleService.java index ddfeba41..b5371552 100644 --- a/src/com/android/providers/downloads/DownloadIdleService.java +++ b/src/com/android/providers/downloads/DownloadIdleService.java @@ -29,6 +29,7 @@ import android.os.Environment; import android.provider.Downloads; import android.system.ErrnoException; import android.text.TextUtils; +import android.text.format.DateUtils; import android.util.Slog; import com.android.providers.downloads.StorageUtils.ConcreteFile; @@ -57,10 +58,11 @@ public class DownloadIdleService extends JobService { @Override public void run() { + cleanStale(); cleanOrphans(); jobFinished(mParams, false); } - }; + } @Override public boolean onStartJob(JobParameters params) { @@ -75,7 +77,47 @@ public class DownloadIdleService extends JobService { return false; } - private interface DownloadQuery { + private interface StaleQuery { + final String[] PROJECTION = new String[] { + Downloads.Impl._ID, + Downloads.Impl.COLUMN_STATUS, + Downloads.Impl.COLUMN_LAST_MODIFICATION, + Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI }; + + final int _ID = 0; + } + + /** + * Remove stale downloads that third-party apps probably forgot about. We + * only consider non-visible downloads that haven't been touched in over a + * week. + */ + public void cleanStale() { + final ContentResolver resolver = getContentResolver(); + + final long modifiedBefore = System.currentTimeMillis() - DateUtils.WEEK_IN_MILLIS; + final Cursor cursor = resolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, + StaleQuery.PROJECTION, Downloads.Impl.COLUMN_STATUS + " >= '200' AND " + + Downloads.Impl.COLUMN_LAST_MODIFICATION + " <= '" + modifiedBefore + + "' AND " + Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI + " == '0'", + null, null); + + int count = 0; + try { + while (cursor.moveToNext()) { + final long id = cursor.getLong(StaleQuery._ID); + resolver.delete(ContentUris.withAppendedId( + Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id), null, null); + count++; + } + } finally { + IoUtils.closeQuietly(cursor); + } + + Slog.d(TAG, "Removed " + count + " stale downloads"); + } + + private interface OrphanQuery { final String[] PROJECTION = new String[] { Downloads.Impl._ID, Downloads.Impl._DATA }; @@ -93,10 +135,10 @@ public class DownloadIdleService extends JobService { // Collect known files from database final HashSet fromDb = Sets.newHashSet(); final Cursor cursor = resolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, - DownloadQuery.PROJECTION, null, null, null); + OrphanQuery.PROJECTION, null, null, null); try { while (cursor.moveToNext()) { - final String path = cursor.getString(DownloadQuery._DATA); + final String path = cursor.getString(OrphanQuery._DATA); if (TextUtils.isEmpty(path)) continue; final File file = new File(path); @@ -111,7 +153,7 @@ public class DownloadIdleService extends JobService { // currently mounted device, so remove it from database. // This logic preserves files on external storage while // media is removed. - final long id = cursor.getLong(DownloadQuery._ID); + final long id = cursor.getLong(OrphanQuery._ID); Slog.d(TAG, "Missing " + file + ", deleting " + id); resolver.delete(ContentUris.withAppendedId( Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id), null, null); -- cgit v1.2.3