summaryrefslogtreecommitdiffstats
path: root/src/com/android/providers/downloads/DownloadService.java
diff options
context:
space:
mode:
authorSteve Howard <showard@google.com>2010-09-15 12:29:50 -0700
committerSteve Howard <showard@google.com>2010-09-20 11:34:54 -0700
commit3398db8f3b195959faa2a7cf09918f364432ac28 (patch)
treeb274d3f30d22bb3d56100cb18e3ade5a83c91b71 /src/com/android/providers/downloads/DownloadService.java
parent4bebe75b3e2361d7fb0aa966598c41c45ad9317f (diff)
downloadandroid_packages_providers_DownloadProvider-3398db8f3b195959faa2a7cf09918f364432ac28.tar.gz
android_packages_providers_DownloadProvider-3398db8f3b195959faa2a7cf09918f364432ac28.tar.bz2
android_packages_providers_DownloadProvider-3398db8f3b195959faa2a7cf09918f364432ac28.zip
Fix notification bugs, cleanup DownloadService + DownloadReceiver
This change started out just fixing a few regressions related to notifications: * Browser downloads weren't picking up a title from the determined filename. This was due to my change to default the title field to "" instead of null. * Notification click/hide events weren't being handled properly. This was due to previous change to the URI structure of DownloadProvider. DownloadReceiver needed to be changed to perform queries through /all_downloads URIs, like all other parts of the download manager code. I did some general refactoring of the DownloadReceiver code while I was there. * The code in DownloadNotification wasn't picking up some updates to downloads properly. This was due to my change to make DownloadNotification use the DownloadInfo objects rather than querying the database directly, so that it could make use of info provided by the DownloadThread that didn't go into the DB. Fixing this didn't turn out to be all that complicated, but along the way to figuring this out I made some substantial refactoring in DownloadService which made it much cleaner anyway and eliminated a lot of duplication. That's something that had to happen eventually, so I'm leaving it all in. Change-Id: I847ccf80e3d928c84e36bc24791b33204104e1b2
Diffstat (limited to 'src/com/android/providers/downloads/DownloadService.java')
-rw-r--r--src/com/android/providers/downloads/DownloadService.java545
1 files changed, 128 insertions, 417 deletions
diff --git a/src/com/android/providers/downloads/DownloadService.java b/src/com/android/providers/downloads/DownloadService.java
index b85fb902..62598b7c 100644
--- a/src/com/android/providers/downloads/DownloadService.java
+++ b/src/com/android/providers/downloads/DownloadService.java
@@ -25,12 +25,8 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.database.CharArrayBuffer;
import android.database.ContentObserver;
import android.database.Cursor;
-import android.drm.mobile1.DrmRawContent;
import android.media.IMediaScannerService;
import android.net.Uri;
import android.os.Environment;
@@ -39,27 +35,22 @@ import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.provider.Downloads;
-import android.util.Config;
import android.util.Log;
-import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
import com.google.common.annotations.VisibleForTesting;
import java.io.File;
-import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
/**
* Performs the background downloads requested by applications that use the Downloads provider.
*/
public class DownloadService extends Service {
-
- /* ------------ Constants ------------ */
-
- /* ------------ Members ------------ */
-
/** Observer to get notified when the content observer's data changes */
private DownloadManagerContentObserver mObserver;
@@ -67,12 +58,12 @@ public class DownloadService extends Service {
private DownloadNotification mNotifier;
/**
- * The Service's view of the list of downloads. This is kept independently
- * from the content provider, and the Service only initiates downloads
- * based on this data, so that it can deal with situation where the data
- * in the content provider changes or disappears.
+ * The Service's view of the list of downloads, mapping download IDs to the corresponding info
+ * object. This is kept independently from the content provider, and the Service only initiates
+ * downloads based on this data, so that it can deal with situation where the data in the
+ * content provider changes or disappears.
*/
- private ArrayList<DownloadInfo> mDownloads;
+ private Map<Long, DownloadInfo> mDownloads = Maps.newHashMap();
/**
* The thread that updates the internal download list from the content
@@ -100,21 +91,9 @@ public class DownloadService extends Service {
*/
private IMediaScannerService mMediaScannerService;
- /**
- * Array used when extracting strings from content provider
- */
- private CharArrayBuffer oldChars;
-
- /**
- * Array used when extracting strings from content provider
- */
- private CharArrayBuffer mNewChars;
-
@VisibleForTesting
SystemFacade mSystemFacade;
- /* ------------ Inner Classes ------------ */
-
/**
* Receives notifications when the data in the content provider changes
*/
@@ -183,8 +162,6 @@ public class DownloadService extends Service {
}
}
- /* ------------ Methods ------------ */
-
/**
* Returns an IBinder instance when someone wants to connect to this
* service. Binding to this service is not allowed.
@@ -208,8 +185,6 @@ public class DownloadService extends Service {
mSystemFacade = new RealSystemFacade(this);
}
- mDownloads = Lists.newArrayList();
-
mObserver = new DownloadManagerContentObserver();
getContentResolver().registerContentObserver(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
true, mObserver);
@@ -220,23 +195,20 @@ public class DownloadService extends Service {
mNotifier = new DownloadNotification(this, mSystemFacade);
mSystemFacade.cancelAllNotifications();
- mNotifier.updateNotification(mDownloads);
trimDatabase();
removeSpuriousFiles();
updateFromProvider();
}
- /**
- * Responds to a call to startService
- */
- public void onStart(Intent intent, int startId) {
- super.onStart(intent, startId);
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ int returnValue = super.onStartCommand(intent, flags, startId);
if (Constants.LOGVV) {
Log.v(Constants.TAG, "Service onStart");
}
-
updateFromProvider();
+ return returnValue;
}
/**
@@ -287,189 +259,101 @@ public class DownloadService extends Service {
stopSelf();
}
if (wakeUp != Long.MAX_VALUE) {
- AlarmManager alarms =
- (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- if (alarms == null) {
- Log.e(Constants.TAG, "couldn't get alarm manager");
- } else {
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "scheduling retry in " + wakeUp + "ms");
- }
- Intent intent = new Intent(Constants.ACTION_RETRY);
- intent.setClassName("com.android.providers.downloads",
- DownloadReceiver.class.getName());
- alarms.set(
- AlarmManager.RTC_WAKEUP,
- mSystemFacade.currentTimeMillis() + wakeUp,
- PendingIntent.getBroadcast(DownloadService.this, 0, intent,
- PendingIntent.FLAG_ONE_SHOT));
- }
+ scheduleAlarm(wakeUp);
}
- oldChars = null;
- mNewChars = null;
return;
}
mPendingUpdate = false;
}
+
long now = mSystemFacade.currentTimeMillis();
+ boolean mustScan = false;
+ keepService = false;
+ wakeUp = Long.MAX_VALUE;
+ Set<Long> idsNoLongerInDatabase = new HashSet<Long>(mDownloads.keySet());
Cursor cursor = getContentResolver().query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
- null, null, null, Downloads.Impl._ID);
-
+ null, null, null, null);
if (cursor == null) {
- // TODO: this doesn't look right, it'd leave the loop in an inconsistent state
- return;
+ continue;
}
+ try {
+ DownloadInfo.Reader reader =
+ new DownloadInfo.Reader(getContentResolver(), cursor);
+ int idColumn = cursor.getColumnIndexOrThrow(Downloads.Impl._ID);
+
+ for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+ long id = cursor.getLong(idColumn);
+ idsNoLongerInDatabase.remove(id);
+ DownloadInfo info = mDownloads.get(id);
+ if (info != null) {
+ updateDownload(reader, info, now);
+ } else {
+ info = insertDownload(reader, now);
+ }
- cursor.moveToFirst();
-
- int arrayPos = 0;
-
- boolean mustScan = false;
- keepService = false;
- wakeUp = Long.MAX_VALUE;
-
- boolean isAfterLast = cursor.isAfterLast();
-
- int idColumn = cursor.getColumnIndexOrThrow(Downloads.Impl._ID);
-
- /*
- * Walk the cursor and the local array to keep them in sync. The key
- * to the algorithm is that the ids are unique and sorted both in
- * the cursor and in the array, so that they can be processed in
- * order in both sources at the same time: at each step, both
- * sources point to the lowest id that hasn't been processed from
- * that source, and the algorithm processes the lowest id from
- * those two possibilities.
- * At each step:
- * -If the array contains an entry that's not in the cursor, remove the
- * entry, move to next entry in the array.
- * -If the array contains an entry that's in the cursor, nothing to do,
- * move to next cursor row and next array entry.
- * -If the cursor contains an entry that's not in the array, insert
- * a new entry in the array, move to next cursor row and next
- * array entry.
- */
- while (!isAfterLast || arrayPos < mDownloads.size()) {
- if (isAfterLast) {
- // We're beyond the end of the cursor but there's still some
- // stuff in the local array, which can only be junk
- if (Constants.LOGVV) {
- int arrayId = ((DownloadInfo) mDownloads.get(arrayPos)).mId;
- Log.v(Constants.TAG, "Array update: trimming " +
- arrayId + " @ " + arrayPos);
+ if (info.shouldScanFile() && !scanFile(info, true)) {
+ mustScan = true;
+ keepService = true;
}
- if (shouldScanFile(arrayPos) && mediaScannerConnected()) {
- scanFile(null, arrayPos);
+ if (info.hasCompletionNotification()) {
+ keepService = true;
}
- deleteDownload(arrayPos); // this advances in the array
- } else {
- int id = cursor.getInt(idColumn);
-
- if (arrayPos == mDownloads.size()) {
- insertDownload(cursor, arrayPos, now);
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Array update: appending " +
- id + " @ " + arrayPos);
- }
- if (shouldScanFile(arrayPos)
- && (!mediaScannerConnected() || !scanFile(cursor, arrayPos))) {
- mustScan = true;
- keepService = true;
- }
- if (visibleNotification(arrayPos)) {
- keepService = true;
- }
- long next = nextAction(arrayPos, now);
- if (next == 0) {
- keepService = true;
- } else if (next > 0 && next < wakeUp) {
- wakeUp = next;
- }
- ++arrayPos;
- cursor.moveToNext();
- isAfterLast = cursor.isAfterLast();
- } else {
- int arrayId = mDownloads.get(arrayPos).mId;
-
- if (arrayId < id) {
- // The array entry isn't in the cursor
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Array update: removing " + arrayId
- + " @ " + arrayPos);
- }
- if (shouldScanFile(arrayPos) && mediaScannerConnected()) {
- scanFile(null, arrayPos);
- }
- deleteDownload(arrayPos); // this advances in the array
- } else if (arrayId == id) {
- // This cursor row already exists in the stored array
- updateDownload(cursor, arrayPos, now);
- if (shouldScanFile(arrayPos)
- && (!mediaScannerConnected()
- || !scanFile(cursor, arrayPos))) {
- mustScan = true;
- keepService = true;
- }
- if (visibleNotification(arrayPos)) {
- keepService = true;
- }
- long next = nextAction(arrayPos, now);
- if (next == 0) {
- keepService = true;
- } else if (next > 0 && next < wakeUp) {
- wakeUp = next;
- }
- ++arrayPos;
- cursor.moveToNext();
- isAfterLast = cursor.isAfterLast();
- } else {
- // This cursor entry didn't exist in the stored array
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Array update: inserting " +
- id + " @ " + arrayPos);
- }
- insertDownload(cursor, arrayPos, now);
- if (shouldScanFile(arrayPos)
- && (!mediaScannerConnected()
- || !scanFile(cursor, arrayPos))) {
- mustScan = true;
- keepService = true;
- }
- if (visibleNotification(arrayPos)) {
- keepService = true;
- }
- long next = nextAction(arrayPos, now);
- if (next == 0) {
- keepService = true;
- } else if (next > 0 && next < wakeUp) {
- wakeUp = next;
- }
- ++arrayPos;
- cursor.moveToNext();
- isAfterLast = cursor.isAfterLast();
- }
+ long next = info.nextAction(now);
+ if (next == 0) {
+ keepService = true;
+ } else if (next > 0 && next < wakeUp) {
+ wakeUp = next;
}
}
+ } finally {
+ cursor.close();
+ }
+
+ for (Long id : idsNoLongerInDatabase) {
+ deleteDownload(id);
}
- mNotifier.updateNotification(mDownloads);
+ mNotifier.updateNotification(mDownloads.values());
if (mustScan) {
- if (!mMediaScannerConnecting) {
- Intent intent = new Intent();
- intent.setClassName("com.android.providers.media",
- "com.android.providers.media.MediaScannerService");
- mMediaScannerConnecting = true;
- bindService(intent, mMediaScannerConnection, BIND_AUTO_CREATE);
- }
+ bindMediaScanner();
} else {
mMediaScannerConnection.disconnectMediaScanner();
}
+ }
+ }
- cursor.close();
+ private void bindMediaScanner() {
+ if (!mMediaScannerConnecting) {
+ Intent intent = new Intent();
+ intent.setClassName("com.android.providers.media",
+ "com.android.providers.media.MediaScannerService");
+ mMediaScannerConnecting = true;
+ bindService(intent, mMediaScannerConnection, BIND_AUTO_CREATE);
}
}
+
+ private void scheduleAlarm(long wakeUp) {
+ AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ if (alarms == null) {
+ Log.e(Constants.TAG, "couldn't get alarm manager");
+ return;
+ }
+
+ if (Constants.LOGV) {
+ Log.v(Constants.TAG, "scheduling retry in " + wakeUp + "ms");
+ }
+
+ Intent intent = new Intent(Constants.ACTION_RETRY);
+ intent.setClassName("com.android.providers.downloads",
+ DownloadReceiver.class.getName());
+ alarms.set(
+ AlarmManager.RTC_WAKEUP,
+ mSystemFacade.currentTimeMillis() + wakeUp,
+ PendingIntent.getBroadcast(DownloadService.this, 0, intent,
+ PendingIntent.FLAG_ONE_SHOT));
+ }
}
/**
@@ -546,133 +430,40 @@ public class DownloadService extends Service {
* Keeps a local copy of the info about a download, and initiates the
* download if appropriate.
*/
- private void insertDownload(Cursor cursor, int arrayPos, long now) {
- DownloadInfo info = new DownloadInfo(this, mSystemFacade, cursor);
+ private DownloadInfo insertDownload(DownloadInfo.Reader reader, long now) {
+ DownloadInfo info = reader.newDownloadInfo(this, mSystemFacade);
+ mDownloads.put(info.mId, info);
if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Service adding new entry");
- Log.v(Constants.TAG, "ID : " + info.mId);
- Log.v(Constants.TAG, "URI : " + ((info.mUri != null) ? "yes" : "no"));
- Log.v(Constants.TAG, "NO_INTEG: " + info.mNoIntegrity);
- Log.v(Constants.TAG, "HINT : " + info.mHint);
- Log.v(Constants.TAG, "FILENAME: " + info.mFileName);
- Log.v(Constants.TAG, "MIMETYPE: " + info.mMimeType);
- Log.v(Constants.TAG, "DESTINAT: " + info.mDestination);
- Log.v(Constants.TAG, "VISIBILI: " + info.mVisibility);
- Log.v(Constants.TAG, "CONTROL : " + info.mControl);
- Log.v(Constants.TAG, "STATUS : " + info.mStatus);
- Log.v(Constants.TAG, "FAILED_C: " + info.mNumFailed);
- Log.v(Constants.TAG, "RETRY_AF: " + info.mRetryAfter);
- Log.v(Constants.TAG, "REDIRECT: " + info.mRedirectCount);
- Log.v(Constants.TAG, "LAST_MOD: " + info.mLastMod);
- Log.v(Constants.TAG, "PACKAGE : " + info.mPackage);
- Log.v(Constants.TAG, "CLASS : " + info.mClass);
- Log.v(Constants.TAG, "COOKIES : " + ((info.mCookies != null) ? "yes" : "no"));
- Log.v(Constants.TAG, "AGENT : " + info.mUserAgent);
- Log.v(Constants.TAG, "REFERER : " + ((info.mReferer != null) ? "yes" : "no"));
- Log.v(Constants.TAG, "TOTAL : " + info.mTotalBytes);
- Log.v(Constants.TAG, "CURRENT : " + info.mCurrentBytes);
- Log.v(Constants.TAG, "ETAG : " + info.mETag);
- Log.v(Constants.TAG, "SCANNED : " + info.mMediaScanned);
- }
-
- mDownloads.add(arrayPos, info);
-
- if (info.mStatus == 0
- && (info.mDestination == Downloads.Impl.DESTINATION_EXTERNAL
- || info.mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE)
- && info.mMimeType != null
- && !DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(info.mMimeType)) {
- // Check to see if we are allowed to download this file. Only files
- // that can be handled by the platform can be downloaded.
- // special case DRM files, which we should always allow downloading.
- Intent mimetypeIntent = new Intent(Intent.ACTION_VIEW);
-
- // We can provide data as either content: or file: URIs,
- // so allow both. (I think it would be nice if we just did
- // everything as content: URIs)
- // Actually, right now the download manager's UId restrictions
- // prevent use from using content: so it's got to be file: or
- // nothing
-
- mimetypeIntent.setDataAndType(Uri.fromParts("file", "", null), info.mMimeType);
- ResolveInfo ri = getPackageManager().resolveActivity(mimetypeIntent,
- PackageManager.MATCH_DEFAULT_ONLY);
- //Log.i(Constants.TAG, "*** QUERY " + mimetypeIntent + ": " + list);
-
- if (ri == null) {
- Log.d(Constants.TAG, "no application to handle MIME type " + info.mMimeType);
- info.mStatus = Downloads.Impl.STATUS_NOT_ACCEPTABLE;
-
- ContentValues values = new ContentValues();
- values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_NOT_ACCEPTABLE);
- getContentResolver().update(info.getAllDownloadsUri(), values, null, null);
- info.sendIntentIfRequested();
- return;
- }
+ info.logVerboseInfo();
}
if (info.isReadyToStart(now)) {
info.start(now);
}
+
+ return info;
}
/**
* Updates the local copy of the info about a download.
*/
- private void updateDownload(Cursor cursor, int arrayPos, long now) {
- DownloadInfo info = mDownloads.get(arrayPos);
- int statusColumn = cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_STATUS);
- int failedColumn = cursor.getColumnIndexOrThrow(Constants.FAILED_CONNECTIONS);
- info.mId = cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.Impl._ID));
- info.mUri = stringFromCursor(info.mUri, cursor, Downloads.Impl.COLUMN_URI);
- info.mNoIntegrity = cursor.getInt(cursor.getColumnIndexOrThrow(
- Downloads.Impl.COLUMN_NO_INTEGRITY)) == 1;
- info.mHint = stringFromCursor(info.mHint, cursor, Downloads.Impl.COLUMN_FILE_NAME_HINT);
- info.mFileName = stringFromCursor(info.mFileName, cursor, Downloads.Impl._DATA);
- info.mMimeType = stringFromCursor(info.mMimeType, cursor, Downloads.Impl.COLUMN_MIME_TYPE);
- info.mDestination = cursor.getInt(cursor.getColumnIndexOrThrow(
- Downloads.Impl.COLUMN_DESTINATION));
- int newVisibility = cursor.getInt(cursor.getColumnIndexOrThrow(
- Downloads.Impl.COLUMN_VISIBILITY));
- if (info.mVisibility == Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
- && newVisibility != Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
- && Downloads.Impl.isStatusCompleted(info.mStatus)) {
- mSystemFacade.cancelNotification(info.mId);
- }
- info.mVisibility = newVisibility;
- synchronized (info) {
- info.mControl = cursor.getInt(cursor.getColumnIndexOrThrow(
- Downloads.Impl.COLUMN_CONTROL));
- }
- int newStatus = cursor.getInt(statusColumn);
- if (!Downloads.Impl.isStatusCompleted(info.mStatus) &&
- Downloads.Impl.isStatusCompleted(newStatus)) {
+ private void updateDownload(DownloadInfo.Reader reader, DownloadInfo info, long now) {
+ int oldVisibility = info.mVisibility;
+ int oldStatus = info.mStatus;
+
+ reader.updateFromDatabase(info);
+
+ boolean lostVisibility =
+ oldVisibility == Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
+ && info.mVisibility != Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
+ && Downloads.Impl.isStatusCompleted(info.mStatus);
+ boolean justCompleted =
+ !Downloads.Impl.isStatusCompleted(oldStatus)
+ && Downloads.Impl.isStatusCompleted(info.mStatus);
+ if (lostVisibility || justCompleted) {
mSystemFacade.cancelNotification(info.mId);
}
- info.mStatus = newStatus;
- info.mNumFailed = cursor.getInt(failedColumn);
- int retryRedirect =
- cursor.getInt(cursor.getColumnIndexOrThrow(Constants.RETRY_AFTER_X_REDIRECT_COUNT));
- info.mRetryAfter = retryRedirect & 0xfffffff;
- info.mRedirectCount = retryRedirect >> 28;
- info.mLastMod = cursor.getLong(cursor.getColumnIndexOrThrow(
- Downloads.Impl.COLUMN_LAST_MODIFICATION));
- info.mPackage = stringFromCursor(
- info.mPackage, cursor, Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE);
- info.mClass = stringFromCursor(
- info.mClass, cursor, Downloads.Impl.COLUMN_NOTIFICATION_CLASS);
- info.mCookies = stringFromCursor(info.mCookies, cursor, Downloads.Impl.COLUMN_COOKIE_DATA);
- info.mUserAgent = stringFromCursor(
- info.mUserAgent, cursor, Downloads.Impl.COLUMN_USER_AGENT);
- info.mReferer = stringFromCursor(info.mReferer, cursor, Downloads.Impl.COLUMN_REFERER);
- info.mTotalBytes = cursor.getInt(cursor.getColumnIndexOrThrow(
- Downloads.Impl.COLUMN_TOTAL_BYTES));
- info.mCurrentBytes = cursor.getInt(cursor.getColumnIndexOrThrow(
- Downloads.Impl.COLUMN_CURRENT_BYTES));
- info.mETag = stringFromCursor(info.mETag, cursor, Constants.ETAG);
- info.mMediaScanned =
- cursor.getInt(cursor.getColumnIndexOrThrow(Constants.MEDIA_SCANNED)) == 1;
if (info.isReadyToRestart(now)) {
info.start(now);
@@ -680,128 +471,48 @@ public class DownloadService extends Service {
}
/**
- * Returns a String that holds the current value of the column,
- * optimizing for the case where the value hasn't changed.
- */
- private String stringFromCursor(String old, Cursor cursor, String column) {
- int index = cursor.getColumnIndexOrThrow(column);
- if (old == null) {
- return cursor.getString(index);
- }
- if (mNewChars == null) {
- mNewChars = new CharArrayBuffer(128);
- }
- cursor.copyStringToBuffer(index, mNewChars);
- int length = mNewChars.sizeCopied;
- if (length != old.length()) {
- return cursor.getString(index);
- }
- if (oldChars == null || oldChars.sizeCopied < length) {
- oldChars = new CharArrayBuffer(length);
- }
- char[] oldArray = oldChars.data;
- char[] newArray = mNewChars.data;
- old.getChars(0, length, oldArray, 0);
- for (int i = length - 1; i >= 0; --i) {
- if (oldArray[i] != newArray[i]) {
- return new String(newArray, 0, length);
- }
- }
- return old;
- }
-
- /**
* Removes the local copy of the info about a download.
*/
- private void deleteDownload(int arrayPos) {
- DownloadInfo info = (DownloadInfo) mDownloads.get(arrayPos);
+ private void deleteDownload(long id) {
+ DownloadInfo info = mDownloads.get(id);
+ if (info.shouldScanFile()) {
+ scanFile(info, false);
+ }
if (info.mStatus == Downloads.Impl.STATUS_RUNNING) {
info.mStatus = Downloads.Impl.STATUS_CANCELED;
- } else if (info.mDestination != Downloads.Impl.DESTINATION_EXTERNAL
- && info.mFileName != null) {
+ }
+ if (info.mDestination != Downloads.Impl.DESTINATION_EXTERNAL && info.mFileName != null) {
new File(info.mFileName).delete();
}
mSystemFacade.cancelNotification(info.mId);
-
- mDownloads.remove(arrayPos);
- }
-
- /**
- * Returns the amount of time (as measured from the "now" parameter)
- * at which a download will be active.
- * 0 = immediately - service should stick around to handle this download.
- * -1 = never - service can go away without ever waking up.
- * positive value - service must wake up in the future, as specified in ms from "now"
- */
- private long nextAction(int arrayPos, long now) {
- DownloadInfo info = (DownloadInfo) mDownloads.get(arrayPos);
- if (Downloads.Impl.isStatusCompleted(info.mStatus)) {
- return -1;
- }
- if (info.mStatus != Downloads.Impl.STATUS_RUNNING_PAUSED) {
- return 0;
- }
- if (info.mNumFailed == 0) {
- return 0;
- }
- long when = info.restartTime();
- if (when <= now) {
- return 0;
- }
- return when - now;
- }
-
- /**
- * Returns whether there's a visible notification for this download
- */
- private boolean visibleNotification(int arrayPos) {
- DownloadInfo info = (DownloadInfo) mDownloads.get(arrayPos);
- return info.hasCompletionNotification();
- }
-
- /**
- * Returns whether a file should be scanned
- */
- private boolean shouldScanFile(int arrayPos) {
- DownloadInfo info = (DownloadInfo) mDownloads.get(arrayPos);
- return !info.mMediaScanned
- && info.mDestination == Downloads.Impl.DESTINATION_EXTERNAL
- && Downloads.Impl.isStatusSuccess(info.mStatus)
- && !DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(info.mMimeType);
- }
-
- /**
- * Returns whether we have a live connection to the Media Scanner
- */
- private boolean mediaScannerConnected() {
- return mMediaScannerService != null;
+ mDownloads.remove(info.mId);
}
/**
* Attempts to scan the file if necessary.
- * Returns true if the file has been properly scanned.
+ * @return true if the file has been properly scanned.
*/
- private boolean scanFile(Cursor cursor, int arrayPos) {
- DownloadInfo info = mDownloads.get(arrayPos);
+ private boolean scanFile(DownloadInfo info, boolean updateDatabase) {
synchronized (this) {
- if (mMediaScannerService != null) {
- try {
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "Scanning file " + info.mFileName);
- }
- mMediaScannerService.scanFile(info.mFileName, info.mMimeType);
- if (cursor != null) {
- ContentValues values = new ContentValues();
- values.put(Constants.MEDIA_SCANNED, 1);
- getContentResolver().update(info.getAllDownloadsUri(), values, null, null);
- }
- return true;
- } catch (RemoteException e) {
- Log.d(Constants.TAG, "Failed to scan file " + info.mFileName);
+ if (mMediaScannerService == null) {
+ return false;
+ }
+ try {
+ if (Constants.LOGV) {
+ Log.v(Constants.TAG, "Scanning file " + info.mFileName);
+ }
+ mMediaScannerService.scanFile(info.mFileName, info.mMimeType);
+ if (updateDatabase) {
+ ContentValues values = new ContentValues();
+ values.put(Constants.MEDIA_SCANNED, 1);
+ getContentResolver().update(info.getAllDownloadsUri(), values, null, null);
}
+ return true;
+ } catch (RemoteException e) {
+ Log.d(Constants.TAG, "Failed to scan file " + info.mFileName);
+ return false;
}
}
- return false;
}
}