summaryrefslogtreecommitdiffstats
path: root/src/com/android/providers
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/providers')
-rw-r--r--src/com/android/providers/downloads/Constants.java8
-rw-r--r--src/com/android/providers/downloads/DownloadInfo.java65
-rw-r--r--src/com/android/providers/downloads/DownloadNotification.java4
-rw-r--r--src/com/android/providers/downloads/DownloadProvider.java79
-rw-r--r--src/com/android/providers/downloads/DownloadReceiver.java13
-rw-r--r--src/com/android/providers/downloads/DownloadService.java121
-rw-r--r--src/com/android/providers/downloads/DownloadThread.java25
-rw-r--r--src/com/android/providers/downloads/Helpers.java38
8 files changed, 176 insertions, 177 deletions
diff --git a/src/com/android/providers/downloads/Constants.java b/src/com/android/providers/downloads/Constants.java
index 5cf13531..da2c2fd9 100644
--- a/src/com/android/providers/downloads/Constants.java
+++ b/src/com/android/providers/downloads/Constants.java
@@ -16,7 +16,6 @@
package com.android.providers.downloads;
-import android.util.Config;
import android.util.Log;
/**
@@ -144,11 +143,10 @@ public class Constants {
static final boolean LOGX = false;
/** Enable verbose logging - use with "setprop log.tag.DownloadManager VERBOSE" */
- private static final boolean LOCAL_LOGV = false;
- public static final boolean LOGV = Config.LOGV
- || (Config.LOGD && LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE));
+ private static final boolean LOCAL_LOGV = true; // STOPSHIP change this to false before shipping
+ public static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE);
/** Enable super-verbose logging */
- private static final boolean LOCAL_LOGVV = false;
+ private static final boolean LOCAL_LOGVV = true; // STOPSHIP change this to false before shipping
public static final boolean LOGVV = LOCAL_LOGVV && LOGV;
}
diff --git a/src/com/android/providers/downloads/DownloadInfo.java b/src/com/android/providers/downloads/DownloadInfo.java
index 36816b59..363b68cd 100644
--- a/src/com/android/providers/downloads/DownloadInfo.java
+++ b/src/com/android/providers/downloads/DownloadInfo.java
@@ -45,8 +45,6 @@ public class DownloadInfo {
public static class Reader {
private ContentResolver mResolver;
private Cursor mCursor;
- private CharArrayBuffer mOldChars;
- private CharArrayBuffer mNewChars;
public Reader(ContentResolver resolver, Cursor cursor) {
mResolver = resolver;
@@ -62,11 +60,11 @@ public class DownloadInfo {
public void updateFromDatabase(DownloadInfo info) {
info.mId = getLong(Downloads.Impl._ID);
- info.mUri = getString(info.mUri, Downloads.Impl.COLUMN_URI);
+ info.mUri = getString(Downloads.Impl.COLUMN_URI);
info.mNoIntegrity = getInt(Downloads.Impl.COLUMN_NO_INTEGRITY) == 1;
- info.mHint = getString(info.mHint, Downloads.Impl.COLUMN_FILE_NAME_HINT);
- info.mFileName = getString(info.mFileName, Downloads.Impl._DATA);
- info.mMimeType = getString(info.mMimeType, Downloads.Impl.COLUMN_MIME_TYPE);
+ info.mHint = getString(Downloads.Impl.COLUMN_FILE_NAME_HINT);
+ info.mFileName = getString(Downloads.Impl._DATA);
+ info.mMimeType = getString(Downloads.Impl.COLUMN_MIME_TYPE);
info.mDestination = getInt(Downloads.Impl.COLUMN_DESTINATION);
info.mVisibility = getInt(Downloads.Impl.COLUMN_VISIBILITY);
info.mStatus = getInt(Downloads.Impl.COLUMN_STATUS);
@@ -75,24 +73,23 @@ public class DownloadInfo {
info.mRetryAfter = retryRedirect & 0xfffffff;
info.mRedirectCount = retryRedirect >> 28;
info.mLastMod = getLong(Downloads.Impl.COLUMN_LAST_MODIFICATION);
- info.mPackage = getString(info.mPackage, Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE);
- info.mClass = getString(info.mClass, Downloads.Impl.COLUMN_NOTIFICATION_CLASS);
- info.mExtras = getString(info.mExtras, Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS);
- info.mCookies = getString(info.mCookies, Downloads.Impl.COLUMN_COOKIE_DATA);
- info.mUserAgent = getString(info.mUserAgent, Downloads.Impl.COLUMN_USER_AGENT);
- info.mReferer = getString(info.mReferer, Downloads.Impl.COLUMN_REFERER);
+ info.mPackage = getString(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE);
+ info.mClass = getString(Downloads.Impl.COLUMN_NOTIFICATION_CLASS);
+ info.mExtras = getString(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS);
+ info.mCookies = getString(Downloads.Impl.COLUMN_COOKIE_DATA);
+ info.mUserAgent = getString(Downloads.Impl.COLUMN_USER_AGENT);
+ info.mReferer = getString(Downloads.Impl.COLUMN_REFERER);
info.mTotalBytes = getLong(Downloads.Impl.COLUMN_TOTAL_BYTES);
info.mCurrentBytes = getLong(Downloads.Impl.COLUMN_CURRENT_BYTES);
- info.mETag = getString(info.mETag, Constants.ETAG);
+ info.mETag = getString(Constants.ETAG);
info.mMediaScanned = getInt(Constants.MEDIA_SCANNED) == 1;
info.mDeleted = getInt(Downloads.Impl.COLUMN_DELETED) == 1;
- info.mMediaProviderUri = getString(info.mMediaProviderUri,
- Downloads.Impl.COLUMN_MEDIAPROVIDER_URI);
+ info.mMediaProviderUri = getString(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI);
info.mIsPublicApi = getInt(Downloads.Impl.COLUMN_IS_PUBLIC_API) != 0;
info.mAllowedNetworkTypes = getInt(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES);
info.mAllowRoaming = getInt(Downloads.Impl.COLUMN_ALLOW_ROAMING) != 0;
- info.mTitle = getString(info.mTitle, Downloads.Impl.COLUMN_TITLE);
- info.mDescription = getString(info.mDescription, Downloads.Impl.COLUMN_DESCRIPTION);
+ info.mTitle = getString(Downloads.Impl.COLUMN_TITLE);
+ info.mDescription = getString(Downloads.Impl.COLUMN_DESCRIPTION);
info.mBypassRecommendedSizeLimit =
getInt(Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT);
@@ -130,35 +127,10 @@ public class DownloadInfo {
info.mRequestHeaders.add(Pair.create(header, value));
}
- /**
- * Returns a String that holds the current value of the column, optimizing for the case
- * where the value hasn't changed.
- */
- private String getString(String old, String column) {
+ private String getString(String column) {
int index = mCursor.getColumnIndexOrThrow(column);
- if (old == null) {
- return mCursor.getString(index);
- }
- if (mNewChars == null) {
- mNewChars = new CharArrayBuffer(128);
- }
- mCursor.copyStringToBuffer(index, mNewChars);
- int length = mNewChars.sizeCopied;
- if (length != old.length()) {
- return new String(mNewChars.data, 0, length);
- }
- if (mOldChars == null || mOldChars.sizeCopied < length) {
- mOldChars = new CharArrayBuffer(length);
- }
- char[] oldArray = mOldChars.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;
+ String s = mCursor.getString(index);
+ return (TextUtils.isEmpty(s)) ? null : s;
}
private Integer getInt(String column) {
@@ -547,7 +519,8 @@ public class DownloadInfo {
*/
boolean shouldScanFile() {
return !mMediaScanned
- && mDestination == Downloads.Impl.DESTINATION_EXTERNAL
+ && (mDestination == Downloads.Impl.DESTINATION_EXTERNAL ||
+ mDestination == Downloads.Impl.DESTINATION_FILE_URI)
&& Downloads.Impl.isStatusSuccess(mStatus)
&& !DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(mMimeType);
}
diff --git a/src/com/android/providers/downloads/DownloadNotification.java b/src/com/android/providers/downloads/DownloadNotification.java
index 4d615df7..4002a97a 100644
--- a/src/com/android/providers/downloads/DownloadNotification.java
+++ b/src/com/android/providers/downloads/DownloadNotification.java
@@ -259,12 +259,12 @@ class DownloadNotification {
private boolean isActiveAndVisible(DownloadInfo download) {
return 100 <= download.mStatus && download.mStatus < 200
- && download.mVisibility != Downloads.VISIBILITY_HIDDEN;
+ && download.mVisibility != Downloads.Impl.VISIBILITY_HIDDEN;
}
private boolean isCompleteAndVisible(DownloadInfo download) {
return download.mStatus >= 200
- && download.mVisibility == Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED;
+ && download.mVisibility == Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED;
}
/*
diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java
index d97d6189..9336b737 100644
--- a/src/com/android/providers/downloads/DownloadProvider.java
+++ b/src/com/android/providers/downloads/DownloadProvider.java
@@ -16,6 +16,7 @@
package com.android.providers.downloads;
+import android.app.DownloadManager;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -25,10 +26,8 @@ import android.content.UriMatcher;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.CrossProcessCursor;
import android.database.Cursor;
-import android.database.CursorWindow;
-import android.database.CursorWrapper;
+import android.database.DatabaseUtils;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
@@ -45,11 +44,11 @@ import com.google.common.annotations.VisibleForTesting;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
@@ -59,7 +58,7 @@ public final class DownloadProvider extends ContentProvider {
/** Database filename */
private static final String DB_NAME = "downloads.db";
/** Current database version */
- private static final int DB_VERSION = 106;
+ private static final int DB_VERSION = 107;
/** Name of table in the database */
private static final String DB_TABLE = "downloads";
@@ -80,6 +79,11 @@ public final class DownloadProvider extends ContentProvider {
private static final int ALL_DOWNLOADS_ID = 4;
/** URI matcher constant for the URI of a download's request headers */
private static final int REQUEST_HEADERS_URI = 5;
+ /** URI matcher constant for the public URI returned by
+ * {@link DownloadManager#getUriForDownloadedFile(long)} if the given downloaded file
+ * is publicly accessible.
+ */
+ private static final int PUBLIC_DOWNLOAD_ID = 6;
static {
sURIMatcher.addURI("downloads", "my_downloads", MY_DOWNLOADS);
sURIMatcher.addURI("downloads", "my_downloads/#", MY_DOWNLOADS_ID);
@@ -97,6 +101,9 @@ public final class DownloadProvider extends ContentProvider {
sURIMatcher.addURI("downloads",
"download/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
REQUEST_HEADERS_URI);
+ sURIMatcher.addURI("downloads",
+ Downloads.Impl.PUBLICLY_ACCESSIBLE_DOWNLOADS_URI_SEGMENT + "/#",
+ PUBLIC_DOWNLOAD_ID);
}
/** Different base URIs that could be used to access an individual download */
@@ -135,6 +142,8 @@ public final class DownloadProvider extends ContentProvider {
sAppReadableColumnsSet.add(sAppReadableColumnsArray[i]);
}
}
+ private static final List<String> downloadManagerColumnsList =
+ Arrays.asList(DownloadManager.UNDERLYING_COLUMNS);
/** The database that lies underneath this content provider */
private SQLiteOpenHelper mOpenHelper = null;
@@ -279,6 +288,10 @@ public final class DownloadProvider extends ContentProvider {
"BOOLEAN NOT NULL DEFAULT 0");
break;
+ case 107:
+ addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ERROR_MSG, "TEXT");
+ break;
+
default:
throw new IllegalStateException("Don't know how to upgrade to " + version);
}
@@ -425,6 +438,15 @@ public final class DownloadProvider extends ContentProvider {
case MY_DOWNLOADS_ID: {
return DOWNLOAD_TYPE;
}
+ case PUBLIC_DOWNLOAD_ID: {
+ // return the mimetype of this id from the database
+ final String id = getDownloadIdFromUri(uri);
+ final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ return DatabaseUtils.stringForQuery(db,
+ "SELECT " + Downloads.Impl.COLUMN_MIME_TYPE + " FROM " + DB_TABLE +
+ " WHERE " + Downloads.Impl._ID + " = ?",
+ new String[]{id});
+ }
default: {
if (Constants.LOGV) {
Log.v(Constants.TAG, "calling getType on an unknown URI: " + uri);
@@ -645,6 +667,7 @@ public final class DownloadProvider extends ContentProvider {
values.remove(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES);
values.remove(Downloads.Impl.COLUMN_ALLOW_ROAMING);
values.remove(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI);
+ values.remove(Downloads.Impl.COLUMN_MEDIA_SCANNED);
Iterator<Map.Entry<String, Object>> iterator = values.valueSet().iterator();
while (iterator.hasNext()) {
String key = iterator.next().getKey();
@@ -720,8 +743,10 @@ public final class DownloadProvider extends ContentProvider {
if (projection == null) {
projection = sAppReadableColumnsArray;
} else {
+ // check the validity of the columns in projection
for (int i = 0; i < projection.length; ++i) {
- if (!sAppReadableColumnsSet.contains(projection[i])) {
+ if (!sAppReadableColumnsSet.contains(projection[i]) &&
+ !downloadManagerColumnsList.contains(projection[i])) {
throw new IllegalArgumentException(
"column " + projection[i] + " is not allowed in queries");
}
@@ -737,10 +762,6 @@ public final class DownloadProvider extends ContentProvider {
fullSelection.getParameters(), null, null, sort);
if (ret != null) {
- ret = new ReadOnlyCursorWrapper(ret);
- }
-
- if (ret != null) {
ret.setNotificationUri(getContext().getContentResolver(), uri);
if (Constants.LOGVV) {
Log.v(Constants.TAG,
@@ -831,9 +852,8 @@ public final class DownloadProvider extends ContentProvider {
+ getDownloadIdFromUri(uri);
String[] projection = new String[] {Downloads.Impl.RequestHeaders.COLUMN_HEADER,
Downloads.Impl.RequestHeaders.COLUMN_VALUE};
- Cursor cursor = db.query(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, projection, where,
- null, null, null, null);
- return new ReadOnlyCursorWrapper(cursor);
+ return db.query(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, projection, where,
+ null, null, null, null);
}
/**
@@ -972,7 +992,8 @@ public final class DownloadProvider extends ContentProvider {
int uriMatch) {
SqlSelection selection = new SqlSelection();
selection.appendClause(where, whereArgs);
- if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID) {
+ if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID ||
+ uriMatch == PUBLIC_DOWNLOAD_ID) {
selection.appendClause(Downloads.Impl._ID + " = ?", getDownloadIdFromUri(uri));
}
if ((uriMatch == MY_DOWNLOADS || uriMatch == MY_DOWNLOADS_ID)
@@ -1128,34 +1149,4 @@ public final class DownloadProvider extends ContentProvider {
to.put(key, defaultValue);
}
}
-
- private class ReadOnlyCursorWrapper extends CursorWrapper implements CrossProcessCursor {
- public ReadOnlyCursorWrapper(Cursor cursor) {
- super(cursor);
- mCursor = (CrossProcessCursor) cursor;
- }
-
- public boolean deleteRow() {
- throw new SecurityException("Download manager cursors are read-only");
- }
-
- public boolean commitUpdates() {
- throw new SecurityException("Download manager cursors are read-only");
- }
-
- public void fillWindow(int pos, CursorWindow window) {
- mCursor.fillWindow(pos, window);
- }
-
- public CursorWindow getWindow() {
- return mCursor.getWindow();
- }
-
- public boolean onMove(int oldPosition, int newPosition) {
- return mCursor.onMove(oldPosition, newPosition);
- }
-
- private CrossProcessCursor mCursor;
- }
-
}
diff --git a/src/com/android/providers/downloads/DownloadReceiver.java b/src/com/android/providers/downloads/DownloadReceiver.java
index c41895b9..33066393 100644
--- a/src/com/android/providers/downloads/DownloadReceiver.java
+++ b/src/com/android/providers/downloads/DownloadReceiver.java
@@ -41,6 +41,7 @@ public class DownloadReceiver extends BroadcastReceiver {
@VisibleForTesting
SystemFacade mSystemFacade = null;
+ @Override
public void onReceive(Context context, Intent intent) {
if (mSystemFacade == null) {
mSystemFacade = new RealSystemFacade(context);
@@ -169,11 +170,21 @@ public class DownloadReceiver extends BroadcastReceiver {
if (isPublicApi) {
appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED);
appIntent.setPackage(pckg);
+ // send id of the items clicked on.
+ if (intent.getBooleanExtra("multiple", false)) {
+ // broadcast received saying click occurred on a notification with multiple titles.
+ // don't include any ids at all - let the caller query all downloads belonging to it
+ // TODO modify the broadcast to include ids of those multiple notifications.
+ } else {
+ appIntent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS,
+ new long[] {
+ cursor.getLong(cursor.getColumnIndexOrThrow(Downloads.Impl._ID))});
+ }
} else { // legacy behavior
if (clazz == null) {
return;
}
- appIntent = new Intent(Downloads.Impl.ACTION_NOTIFICATION_CLICKED);
+ appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED);
appIntent.setClassName(pckg, clazz);
if (intent.getBooleanExtra("multiple", true)) {
appIntent.setData(Downloads.Impl.CONTENT_URI);
diff --git a/src/com/android/providers/downloads/DownloadService.java b/src/com/android/providers/downloads/DownloadService.java
index 169ef970..f93c5c2e 100644
--- a/src/com/android/providers/downloads/DownloadService.java
+++ b/src/com/android/providers/downloads/DownloadService.java
@@ -28,6 +28,7 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.database.ContentObserver;
import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
import android.media.IMediaScannerListener;
import android.media.IMediaScannerService;
import android.net.Uri;
@@ -44,7 +45,6 @@ import com.google.android.collect.Maps;
import com.google.common.annotations.VisibleForTesting;
import java.io.File;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
@@ -114,6 +114,7 @@ public class DownloadService extends Service {
* Receives notification when the data in the observed content
* provider changes.
*/
+ @Override
public void onChange(final boolean selfChange) {
if (Constants.LOGVV) {
Log.v(Constants.TAG, "Service ContentObserver received notification");
@@ -188,6 +189,7 @@ public class DownloadService extends Service {
*
* @throws UnsupportedOperationException
*/
+ @Override
public IBinder onBind(Intent i) {
throw new UnsupportedOperationException("Cannot bind to Download Manager Service");
}
@@ -195,6 +197,7 @@ public class DownloadService extends Service {
/**
* Initializes the service when it is first created
*/
+ @Override
public void onCreate() {
super.onCreate();
if (Constants.LOGVV) {
@@ -232,6 +235,7 @@ public class DownloadService extends Service {
/**
* Cleans up when the service is destroyed
*/
+ @Override
public void onDestroy() {
getContentResolver().unregisterContentObserver(mObserver);
if (Constants.LOGVV) {
@@ -258,6 +262,7 @@ public class DownloadService extends Service {
super("Download Service");
}
+ @Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
@@ -363,24 +368,23 @@ public class DownloadService extends Service {
if (info.shouldScanFile()) {
// initiate rescan of the file to - which will populate
// mediaProviderUri column in this row
- if (!scanFile(info, true, false)) {
+ if (!scanFile(info, false, true)) {
throw new IllegalStateException("scanFile failed!");
}
- } else {
- // this file should NOT be scanned. delete the file.
- Helpers.deleteFile(getContentResolver(), info.mId, info.mFileName,
- info.mMimeType);
+ continue;
}
} else {
// yes it has mediaProviderUri column already filled in.
- // delete it from MediaProvider database and then from downloads table
- // in DownProvider database (the order of deletion is important).
+ // delete it from MediaProvider database.
getContentResolver().delete(Uri.parse(info.mMediaProviderUri), null,
null);
- getContentResolver().delete(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
- Downloads.Impl._ID + " = ? ",
- new String[]{String.valueOf(info.mId)});
}
+ // delete the file
+ deleteFileIfExists(info.mFileName);
+ // delete from the downloads db
+ getContentResolver().delete(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
+ Downloads.Impl._ID + " = ? ",
+ new String[]{String.valueOf(info.mId)});
}
}
}
@@ -463,29 +467,41 @@ public class DownloadService extends Service {
* Drops old rows from the database to prevent it from growing too large
*/
private void trimDatabase() {
- Cursor cursor = getContentResolver().query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
- new String[] { Downloads.Impl._ID },
- Downloads.Impl.COLUMN_STATUS + " >= '200'", null,
- Downloads.Impl.COLUMN_LAST_MODIFICATION);
- if (cursor == null) {
- // This isn't good - if we can't do basic queries in our database, nothing's gonna work
- Log.e(Constants.TAG, "null cursor in trimDatabase");
- return;
- }
- if (cursor.moveToFirst()) {
- int numDelete = cursor.getCount() - Constants.MAX_DOWNLOADS;
- int columnId = cursor.getColumnIndexOrThrow(Downloads.Impl._ID);
- while (numDelete > 0) {
- Uri downloadUri = ContentUris.withAppendedId(
- Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, cursor.getLong(columnId));
- getContentResolver().delete(downloadUri, null, null);
- if (!cursor.moveToNext()) {
- break;
+ Cursor cursor = null;
+ try {
+ cursor = getContentResolver().query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
+ new String[] { Downloads.Impl._ID },
+ Downloads.Impl.COLUMN_STATUS + " >= '200'", null,
+ Downloads.Impl.COLUMN_LAST_MODIFICATION);
+ if (cursor == null) {
+ // This isn't good - if we can't do basic queries in our database, nothing's gonna work
+ Log.e(Constants.TAG, "null cursor in trimDatabase");
+ return;
+ }
+ if (cursor.moveToFirst()) {
+ int numDelete = cursor.getCount() - Constants.MAX_DOWNLOADS;
+ int columnId = cursor.getColumnIndexOrThrow(Downloads.Impl._ID);
+ while (numDelete > 0) {
+ Uri downloadUri = ContentUris.withAppendedId(
+ Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, cursor.getLong(columnId));
+ getContentResolver().delete(downloadUri, null, null);
+ if (!cursor.moveToNext()) {
+ break;
+ }
+ numDelete--;
}
- numDelete--;
+ }
+ } catch (SQLiteException e) {
+ // trimming the database raised an exception. alright, ignore the exception
+ // and return silently. trimming database is not exactly a critical operation
+ // and there is no need to propagate the exception.
+ Log.w(Constants.TAG, "trimDatabase failed with exception: " + e.getMessage());
+ return;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
}
}
- cursor.close();
}
/**
@@ -580,22 +596,28 @@ public class DownloadService extends Service {
mMediaScannerService.requestScanFile(info.mFileName, info.mMimeType,
new IMediaScannerListener.Stub() {
public void scanCompleted(String path, Uri uri) {
- if (uri != null && updateDatabase) {
- // file is scanned and mediaprovider returned uri. store it in downloads
- // table (i.e., update this downloaded file's row)
+ if (updateDatabase) {
+ // Mark this as 'scanned' in the database
+ // so that it is NOT subject to re-scanning by MediaScanner
+ // next time this database row row is encountered
ContentValues values = new ContentValues();
values.put(Constants.MEDIA_SCANNED, 1);
- values.put(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI,
- uri.toString());
+ if (uri != null) {
+ values.put(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI,
+ uri.toString());
+ }
getContentResolver().update(key, values, null, null);
- } else if (uri == null && deleteFile) {
- // callback returned NO uri..that means this file doesn't
- // exist in MediaProvider. but it still needs to be deleted
- // TODO don't scan files that are not scannable by MediaScanner.
- // create a public method in MediaFile.java to return false
- // if the given file's mimetype is not any of the types
- // the mediaprovider is interested in.
- Helpers.deleteFile(resolver, id, path, mimeType);
+ } else if (deleteFile) {
+ if (uri != null) {
+ // use the Uri returned to delete it from the MediaProvider
+ getContentResolver().delete(uri, null, null);
+ }
+ // delete the file and delete its row from the downloads db
+ deleteFileIfExists(path);
+ getContentResolver().delete(
+ Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
+ Downloads.Impl._ID + " = ? ",
+ new String[]{String.valueOf(id)});
}
}
});
@@ -606,4 +628,15 @@ public class DownloadService extends Service {
}
}
}
+
+ private void deleteFileIfExists(String path) {
+ try {
+ if (!TextUtils.isEmpty(path)) {
+ File file = new File(path);
+ file.delete();
+ }
+ } catch (Exception e) {
+ Log.w(Constants.TAG, "file: '" + path + "' couldn't be deleted", e);
+ }
+ }
}
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java
index 0da950ba..a1d91019 100644
--- a/src/com/android/providers/downloads/DownloadThread.java
+++ b/src/com/android/providers/downloads/DownloadThread.java
@@ -24,7 +24,6 @@ import android.content.Intent;
import android.drm.mobile1.DrmRawContent;
import android.net.http.AndroidHttpClient;
import android.net.Proxy;
-import android.net.Uri;
import android.os.FileUtils;
import android.os.PowerManager;
import android.os.Process;
@@ -150,6 +149,7 @@ public class DownloadThread extends Thread {
AndroidHttpClient client = null;
PowerManager.WakeLock wakeLock = null;
int finalStatus = Downloads.Impl.STATUS_UNKNOWN_ERROR;
+ String errorMsg = null;
try {
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -189,11 +189,12 @@ public class DownloadThread extends Thread {
finalStatus = Downloads.Impl.STATUS_SUCCESS;
} catch (StopRequest error) {
// remove the cause before printing, in case it contains PII
- Log.w(Constants.TAG,
- "Aborting request for download " + mInfo.mId + ": " + error.getMessage());
+ errorMsg = "Aborting request for download " + mInfo.mId + ": " + error.getMessage();
+ Log.w(Constants.TAG, errorMsg);
finalStatus = error.mFinalStatus;
// fall through to finally block
} catch (Throwable ex) { //sometimes the socket code throws unchecked exceptions
+ errorMsg = "Exception for id " + mInfo.mId + ": " + ex.getMessage();
Log.w(Constants.TAG, "Exception for id " + mInfo.mId + ": " + ex);
finalStatus = Downloads.Impl.STATUS_UNKNOWN_ERROR;
// falls through to the code that reports an error
@@ -209,7 +210,7 @@ public class DownloadThread extends Thread {
cleanupDestination(state, finalStatus);
notifyDownloadCompleted(finalStatus, state.mCountRetry, state.mRetryAfter,
state.mRedirectCount, state.mGotData, state.mFilename,
- state.mNewUri, state.mMimeType);
+ state.mNewUri, state.mMimeType, errorMsg);
mInfo.mHasActiveThread = false;
}
}
@@ -688,7 +689,8 @@ public class DownloadThread extends Thread {
} else {
finalStatus = Downloads.Impl.STATUS_UNHANDLED_HTTP_CODE;
}
- throw new StopRequest(finalStatus, "http error " + statusCode);
+ throw new StopRequest(finalStatus, "http error " + statusCode + ", mContinuingDownload: " +
+ innerState.mContinuingDownload);
}
/**
@@ -863,9 +865,10 @@ public class DownloadThread extends Thread {
*/
private void notifyDownloadCompleted(
int status, boolean countRetry, int retryAfter, int redirectCount, boolean gotData,
- String filename, String uri, String mimeType) {
+ String filename, String uri, String mimeType, String errorMsg) {
notifyThroughDatabase(
- status, countRetry, retryAfter, redirectCount, gotData, filename, uri, mimeType);
+ status, countRetry, retryAfter, redirectCount, gotData, filename, uri, mimeType,
+ errorMsg);
if (Downloads.Impl.isStatusCompleted(status)) {
mInfo.sendIntentIfRequested();
}
@@ -873,7 +876,7 @@ public class DownloadThread extends Thread {
private void notifyThroughDatabase(
int status, boolean countRetry, int retryAfter, int redirectCount, boolean gotData,
- String filename, String uri, String mimeType) {
+ String filename, String uri, String mimeType, String errorMsg) {
ContentValues values = new ContentValues();
values.put(Downloads.Impl.COLUMN_STATUS, status);
values.put(Downloads.Impl._DATA, filename);
@@ -890,7 +893,11 @@ public class DownloadThread extends Thread {
} else {
values.put(Constants.FAILED_CONNECTIONS, mInfo.mNumFailed + 1);
}
-
+ // STOPSHIP begin delete the following lines
+ if (!TextUtils.isEmpty(errorMsg)) {
+ values.put(Downloads.Impl.COLUMN_ERROR_MSG, errorMsg);
+ }
+ // STOPSHIP end
mContext.getContentResolver().update(mInfo.getAllDownloadsUri(), values, null, null);
}
diff --git a/src/com/android/providers/downloads/Helpers.java b/src/com/android/providers/downloads/Helpers.java
index a20a7592..59cc97cf 100644
--- a/src/com/android/providers/downloads/Helpers.java
+++ b/src/com/android/providers/downloads/Helpers.java
@@ -16,7 +16,6 @@
package com.android.providers.downloads;
-import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
@@ -99,25 +98,22 @@ public class Helpers {
boolean isPublicApi) throws GenerateSaveFileError {
checkCanHandleDownload(context, mimeType, destination, isPublicApi);
if (destination == Downloads.Impl.DESTINATION_FILE_URI) {
- return getPathForFileUri(hint, contentLength);
+ String path = verifyFileUri(hint, contentLength);
+ String c = getFullPath(path, mimeType, destination, null);
+ return c;
} else {
return chooseFullPath(context, url, hint, contentDisposition, contentLocation, mimeType,
destination, contentLength);
}
}
- private static String getPathForFileUri(String hint, long contentLength)
+ private static String verifyFileUri(String hint, long contentLength)
throws GenerateSaveFileError {
if (!isExternalMediaMounted()) {
throw new GenerateSaveFileError(Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR,
"external media not mounted");
}
String path = Uri.parse(hint).getPath();
- if (new File(path).exists()) {
- Log.d(Constants.TAG, "File already exists: " + path);
- throw new GenerateSaveFileError(Downloads.Impl.STATUS_FILE_ALREADY_EXISTS_ERROR,
- "requested destination file already exists");
- }
if (getAvailableBytes(getFilesystemRoot(path)) < contentLength) {
throw new GenerateSaveFileError(Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR,
"insufficient space on external storage");
@@ -148,11 +144,15 @@ public class Helpers {
File base = locateDestinationDirectory(context, mimeType, destination, contentLength);
String filename = chooseFilename(url, hint, contentDisposition, contentLocation,
destination);
+ return getFullPath(filename, mimeType, destination, base);
+ }
+ private static String getFullPath(String filename, String mimeType, int destination,
+ File base) throws GenerateSaveFileError {
// Split filename between base and extension
// Add an extension if filename does not have one
String extension = null;
- int dotIndex = filename.indexOf('.');
+ int dotIndex = filename.lastIndexOf('.');
if (dotIndex < 0) {
extension = chooseExtensionFromMimeType(mimeType, true);
} else {
@@ -162,12 +162,13 @@ public class Helpers {
boolean recoveryDir = Constants.RECOVERY_DIRECTORY.equalsIgnoreCase(filename + extension);
- filename = base.getPath() + File.separator + filename;
+ if (base != null) {
+ filename = base.getPath() + File.separator + filename;
+ }
if (Constants.LOGVV) {
Log.v(Constants.TAG, "target file: " + filename + extension);
}
-
return chooseUniqueFilename(destination, filename, extension, recoveryDir);
}
@@ -850,19 +851,4 @@ public class Helpers {
}
return sb.toString();
}
-
- /*
- * Delete the given file from device
- * and delete its row from the downloads database.
- */
- /* package */ static void deleteFile(ContentResolver resolver, long id, String path, String mimeType) {
- try {
- File file = new File(path);
- file.delete();
- } catch (Exception e) {
- Log.w(Constants.TAG, "file: '" + path + "' couldn't be deleted", e);
- }
- resolver.delete(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, Downloads.Impl._ID + " = ? ",
- new String[]{String.valueOf(id)});
- }
}